/** * Creates the factory. */ public ServiceFactory() { try { // service finder used to load the effective finder class serviceFinderClass = new DefaultServiceFinder().findFirstServiceProvider(ServiceFinder.class); } catch (ClassNotFoundException ex) { // we cannot use LOGGER here because this may use the createService as well java.util.logging.Logger.getLogger(ServiceFactory.class.getName()).log(java.util.logging.Level.INFO, "cannot load ServiceFinder class -> fallback to " + DefaultServiceFinder.class.getName(), ex); serviceFinderClass = DefaultServiceFinder.class; } finderMap = new ConcurrentHashMap<>(); explicitLoaders = new HashMap<>(); }
/** * Finds the first service provider by service name.<br> * If similar configurations appear more than once on the classpath, the * first is returned. Useful to override service default implementations. * * @param <T> the service type * @param service the service class * @return the provider class * @throws ClassNotFoundException if some provider could not be loaded */ @SuppressWarnings("unchecked") @Override public <T> Class<T> findFirstServiceProvider(Class<T> service) throws ClassNotFoundException { String serviceName = service.getName(); Map.Entry<String,URL> entry = findFirstServiceConfiguration(serviceName); Class<T> provider = (Class<T>) Class.forName(entry.getKey(), true, getClassLoader(serviceName)); checkProvider(provider, service, entry.getValue()); return provider; }
/** * Finds service configurations by service name.<br> * Iterations over the returned map are ordered by discovery along the classpath. * * @param serviceName the service name, usually a classname * @return a map of the configured services and their corresponding URLs */ @Override public synchronized Map<String,URL> findServiceConfigurations(String serviceName) { return serviceMap.computeIfAbsent(serviceName, sn -> { Map<String,URL> services = new LinkedHashMap<>(); // linked hashmap to preserve order parseURLs(findServiceURLs(sn), sn, services); return services; }); }
/** * Finds the unique service provider by service class.<br> * * @param <T> the service type * @param service the service class * @return the provider class * @throws ClassNotFoundException if some provider could not be loaded */ @SuppressWarnings("unchecked") @Override public <T> Class<T> findUniqueServiceProvider(Class<T> service) throws ClassNotFoundException { String serviceName = service.getName(); Map.Entry<String,URL> entry = findUniqueServiceConfiguration(serviceName); Class<T> provider = (Class<T>) Class.forName(entry.getKey(), true, getClassLoader(serviceName)); checkProvider(provider, service, entry.getValue()); return provider; }
/** * Finds the service providers by service names. * * @param <T> the service type * @param service the service class * @return the classes providing this service * @throws ClassNotFoundException if some provider could not be loaded */ @SuppressWarnings("unchecked") @Override public synchronized <T> Collection<Class<T>> findServiceProviders(Class<T> service) throws ClassNotFoundException { String serviceName = service.getName(); Collection<Class<T>> serviceImplClasses = (Collection) classMap.get(serviceName); if (serviceImplClasses == null) { serviceImplClasses = new ArrayList<>(); for (Map.Entry<String,URL> entry: findServiceConfigurations(serviceName).entrySet()) { String serviceImplName = entry.getKey(); Class<T> provider = (Class<T>) Class.forName(serviceImplName, true, getClassLoader(serviceName)); // check that provides really implements service checkProvider(provider, service, entry.getValue()); serviceImplClasses.add(provider); } classMap.put(serviceName, (Collection) serviceImplClasses); } return serviceImplClasses; }
/** * Gets the classloader for the given service name. * * @param serviceName the service name * @return the loader, never null */ protected ClassLoader getClassLoader(String serviceName) { ClassLoader cl = ServiceFactory.getExplicitClassLoader(servicePath, serviceName); if (cl == null) { cl = getClassLoader(); } return cl; }
/** * Finds the first service configurations by service name.<br> * If similar configurations appear more than once on the classpath, the * first is returned. Useful to override service default implementations. * * @param serviceName the service name, usually a classname * @return the service configuation entry */ @Override public synchronized Map.Entry<String,URL> findFirstServiceConfiguration(String serviceName) { Map<String,URL> services = findServiceConfigurations(serviceName); if (services.isEmpty()) { throw new TentackleRuntimeException("service '" + serviceName + "' not found"); } return services.entrySet().iterator().next(); }
/** * Parses an enumeration of URLs. * * @param urls the URLs * @param serviceName the service name * @param services the destination map */ protected void parseURLs(Collection<URL> urls, String serviceName, Map<String, URL> services) { urls.forEach(url -> parseURL(url, serviceName, services)); }
String line; while ((line = reader.readLine()) != null) { line = normalizeLine(line); if (line != null) { URL otherUrl = services.put(line, url);
/** * Finds URLs of a service by name.<br> * * @param serviceName the service name, usually a classname * @return the service's resource urls */ @Override public Collection<URL> findServiceURLs(String serviceName) { try { Enumeration<URL> resources = getClassLoader(serviceName).getResources(servicePath + serviceName); return ModuleSorter.INSTANCE.sort(resources); } catch (IOException iox) { throw new TentackleRuntimeException("loading service information failed", iox); } }
/** * Finds a unique service configurations by service name.<br> * It is checked that the service is defined exactly once. * Useful for factories, singletons and alike. * * @param serviceName the service name, usually a classname * @return the service configuation entry */ @Override public synchronized Map.Entry<String,URL> findUniqueServiceConfiguration(String serviceName) { Map<String,URL> services = findServiceConfigurations(serviceName); if (services.isEmpty()) { throw new TentackleRuntimeException("service '" + serviceName + "' not found"); } if (services.size() > 1) { StringBuilder msg = new StringBuilder(); msg.append("service '").append(serviceName).append("' defined more than once: "); boolean needComma = false; for (Map.Entry<String,URL> entry: services.entrySet()) { if (needComma) { msg.append(", "); } msg.append(entry.getKey()).append(" in ").append(entry.getValue()); needComma = true; } throw new TentackleRuntimeException(msg.toString()); } return services.entrySet().iterator().next(); }