package com.revolsys.io; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import com.revolsys.collection.map.Maps; import com.revolsys.logging.Logs; import com.revolsys.record.io.format.json.JsonParser; import com.revolsys.util.Property; public class IoFactoryRegistry { static final Map<Class<? extends IoFactory>, Set<IoFactory>> factoriesByClass = new HashMap<>(); static final Map<Class<? extends IoFactory>, Map<String, IoFactory>> factoryByClassAndFileExtension = new HashMap<>(); static final Map<Class<? extends IoFactory>, Map<String, IoFactory>> factoryByClassAndMediaType = new HashMap<>(); static final Map<Class<? extends IoFactory>, Set<String>> mediaTypesByClass = new HashMap<>(); static final Map<Class<? extends IoFactory>, Set<String>> fileExtensionsByClass = new HashMap<>(); static final Map<String, String> mediaTypeByFileExtension = new HashMap<>(); static final Set<IoFactory> factories = new HashSet<>(); static { try { final ClassLoader classLoader = IoFactoryRegistry.class.getClassLoader(); final ServiceLoader<IoFactory> ioFactories = ServiceLoader.load(IoFactory.class, classLoader); for (final IoFactory ioFactory : ioFactories) { try { if (ioFactory.isAvailable()) { addFactory(ioFactory); } } catch (final Throwable e) { Logs.error(IoFactoryRegistry.class, e); } } final String resourceName = "META-INF/" + IoFactory.class.getName() + ".json"; final Enumeration<URL> resources = classLoader.getResources(resourceName); while (resources.hasMoreElements()) { final URL resource = resources.nextElement(); try { final Map<String, Object> config = JsonParser.getMap(resource.openStream()); @SuppressWarnings("unchecked") final List<Map<String, Object>> factories = (List<Map<String, Object>>)config .get("factories"); for (final Map<String, Object> factoryConfig : factories) { try { final String typeClassName = (String)factoryConfig.get("typeClass"); if (Property.hasValue(typeClassName)) { final Class<?> factoryClass = Class.forName(typeClassName, false, classLoader); boolean initialized = false; for (final Method method : factoryClass.getDeclaredMethods()) { final String methodName = method.getName(); if (methodName.equals("ioFactoryInit")) { if (Modifier.isStatic(method.getModifiers())) { if (method.getParameterTypes().length == 0) { if (method.getReturnType() == Void.TYPE) { initialized = true; try { method.invoke(null); } catch (final Throwable e) { Logs.error(factoryClass, e); } } } } } } if (!initialized) { if (IoFactory.class.isAssignableFrom(factoryClass)) { try { final IoFactory factory = (IoFactory)factoryClass.newInstance(); if (factory.isAvailable()) { addFactory(factory); } } catch (final Throwable e) { Logs.debug(factoryClass, "Unable to instantiate factory", e); } } } } } catch (final Throwable e) { Logs.error(IoFactoryRegistry.class, "Unable to add factory: " + factoryConfig, e); } } } catch (final Throwable e) { Logs.error(IoFactoryRegistry.class, "Unable to read resource: " + resource, e); } } } catch (final Throwable e) { Logs.error(IoFactoryRegistry.class, "Unable to read resources", e); } } public static void addFactory(final IoFactory factory) { synchronized (factories) { if (factories.add(factory)) { factory.init(); final Class<? extends IoFactory> factoryClass = factory.getClass(); addFactory(factory, factoryClass); } } } @SuppressWarnings("unchecked") private static void addFactory(final IoFactory factory, final Class<? extends IoFactory> factoryClass) { final Class<?>[] interfaces = factoryClass.getInterfaces(); for (final Class<?> factoryInterface : interfaces) { if (IoFactory.class.isAssignableFrom(factoryInterface)) { final Class<IoFactory> ioInterface = (Class<IoFactory>)factoryInterface; if (Maps.addToSet(factoriesByClass, ioInterface, factory)) { for (final String fileExtension : factory.getFileExtensions()) { Maps.addToTreeSet(fileExtensionsByClass, ioInterface, fileExtension); Maps.put(factoryByClassAndFileExtension, ioInterface, fileExtension, factory); for (final String mediaType : factory.getMediaTypes()) { mediaTypeByFileExtension.put(fileExtension.toLowerCase(), mediaType); } } for (final String mediaType : factory.getMediaTypes()) { Maps.put(factoryByClassAndMediaType, ioInterface, mediaType, factory); Maps.addToTreeSet(mediaTypesByClass, ioInterface, mediaType); } } addFactory(factory, ioInterface); } } final Class<?> superclass = factoryClass.getSuperclass(); if (superclass != null) { if (IoFactory.class.isAssignableFrom(superclass)) { addFactory(factory, (Class<IoFactory>)superclass); } } } public static void clearInstance() { synchronized (IoFactoryRegistry.class) { factoriesByClass.clear(); factoryByClassAndFileExtension.clear(); mediaTypeByFileExtension.clear(); factories.clear(); factoryByClassAndMediaType.clear(); fileExtensionsByClass.clear(); mediaTypesByClass.clear(); } } }