package org.orienteer.core.service; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.ServiceLoader; import org.apache.wicket.guice.GuiceWebApplicationFactory; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.http.WicketFilter; import org.apache.wicket.util.string.Strings; import org.orienteer.core.OrienteerWebApplication; import org.orienteer.core.util.LookupResourceHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.ydn.wicket.wicketorientdb.OrientDbWebApplication; import com.google.common.collect.Iterables; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.name.Names; import com.google.inject.servlet.ServletModule; import com.google.inject.util.Modules; /** * Main module to load properties and application to Guice * * <h1>Properties</h1> * Properties can be retrieved from both files from the local filesystem and * files on the Java classpath. * System property {@link #ORIENTEER_PROPERTIES_QUALIFIER_PROPERTY_NAME} defines * qualifier which should be used in properties lookup. * Highlevel lookup: * <ol> * <li>If there is a qualifier - lookup by this qualifier</li> * <li>If there is no a qualifier - lookup by default qualifier 'orienteer'</li> * <li>If nothing was found - use embedded configuration</li> * </ol> * Order of lookup for a specific qualifier (for example 'myapplication'): * <ol> * <li>lookup of file specified by system property 'myapplication.properties'</li> * <li>lookup of URL specified by system property 'myapplication.properties'</li> * <li>lookup of file 'myapplication.properties' up from current directory</li> * <li>lookup of file 'myapplication.properties' in '~/orienteer/' directory</li> * <li>lookup of resource 'myapplication.properties' in a classpath</li> * </ol> */ public class OrienteerInitModule extends ServletModule { private static final Logger LOG = LoggerFactory.getLogger(OrienteerInitModule.class); public static final String ORIENTEER_PROPERTIES_QUALIFIER_PROPERTY_NAME = "orienteer.qualifier"; public static final String DEFAULT_ORENTEER_PROPERTIES_QUALIFIER = "orienteer"; public static final String PROPERTIES_RESOURCE_PATH_SYSTEM_DEFAULT = "orienteer-default.properties"; public static final String ORIENTDB_KEY_PREFIX="orientdb."; public final static Properties PROPERTIES_DEFAULT = new Properties(); private static final LookupResourceHelper.StackedResourceLookuper STACK_LOOKUPER = new LookupResourceHelper.StackedResourceLookuper(LookupResourceHelper.SystemPropertyFileLookuper.INSTANCE, LookupResourceHelper.SystemPropertyURLLookuper.INSTANCE, LookupResourceHelper.UpDirectoriesFileLookuper.INSTANCE, LookupResourceHelper.DirFileLookuper.CONFIG_DIR_INSTANCE, LookupResourceHelper.SystemPropertyResourceLookuper.INSTANCE); static { InputStream propertiesDefaultInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(PROPERTIES_RESOURCE_PATH_SYSTEM_DEFAULT); try { PROPERTIES_DEFAULT.load(propertiesDefaultInputStream); } catch (IOException ex) { LOG.error("Critical system resource '"+PROPERTIES_RESOURCE_PATH_SYSTEM_DEFAULT+"' was not found. Terminating. "); throw new ExceptionInInitializerError(ex); } } @Override protected void configureServlets() { Map<String, String> params = new HashMap<String, String>(); params.put(WicketFilter.FILTER_MAPPING_PARAM, "/*"); params.put("applicationFactoryClassName", GuiceWebApplicationFactory.class.getName()); params.put("injectorContextAttribute", Injector.class.getName()); bind(WicketFilter.class).in(Singleton.class); filter("/*").through(WicketFilter.class, params); Properties properties = retrieveProperties(); Names.bindProperties(binder(), properties); bindOrientDbProperties(properties); String applicationClass = properties.getProperty("orienteer.application"); Class<? extends OrienteerWebApplication> appClass = OrienteerWebApplication.class; if (applicationClass != null) { try { Class<?> customAppClass = Class.forName(applicationClass); if (OrienteerWebApplication.class.isAssignableFrom(appClass)) { appClass = (Class<? extends OrienteerWebApplication>) customAppClass; } else { LOG.error("Orienteer application class '" + applicationClass + "' is not child class of '" + OrienteerWebApplication.class + "'. Using default."); } } catch (ClassNotFoundException e) { LOG.error("Orienteer application class '" + applicationClass + "' was not found. Using default."); } } bind(appClass).asEagerSingleton(); Provider<? extends OrienteerWebApplication> appProvider = binder().getProvider(appClass); if (!OrienteerWebApplication.class.equals(appClass)) { bind(OrienteerWebApplication.class).toProvider(appProvider); } bind(OrientDbWebApplication.class).toProvider(appProvider); bind(WebApplication.class).toProvider(appProvider); bind(Properties.class).annotatedWith(Orienteer.class).toInstance(properties); install(loadFromClasspath(new OrienteerModule())); } protected void bindOrientDbProperties(Properties properties) { for(String key : properties.stringPropertyNames()) { if(key.startsWith(ORIENTDB_KEY_PREFIX)) { String subKey = key.substring(ORIENTDB_KEY_PREFIX.length()); System.setProperty(subKey, properties.getProperty(key)); } } } /** * Retrieve startup properties * @return not null {@link Properties} */ public static Properties retrieveProperties() { Properties loadedProperties = new Properties(); loadedProperties.putAll(PROPERTIES_DEFAULT); String qualifier = System.getProperty(ORIENTEER_PROPERTIES_QUALIFIER_PROPERTY_NAME); if(!Strings.isEmpty(qualifier)) { LOG.info("Orienteer startup properties qualifier: "+qualifier); Properties qualifierProperties = retrieveProperties(qualifier); if(qualifierProperties!=null) { loadedProperties.putAll(qualifierProperties); loadedProperties = retrieveSystemProperties(loadedProperties); return loadedProperties; } else { LOG.info("Properties for qualifier '"+qualifier+"' was not found."); } } LOG.info("Using default orienteer startup properties qualifier"); Properties defaultQualifierProperties = retrieveProperties(DEFAULT_ORENTEER_PROPERTIES_QUALIFIER); if(defaultQualifierProperties!=null) { loadedProperties.putAll(defaultQualifierProperties); loadedProperties = retrieveSystemProperties(loadedProperties); return loadedProperties; } else { LOG.info("Properties for qualifier '"+qualifier+"' was not found. Using embedded."); loadedProperties = retrieveSystemProperties(loadedProperties); return loadedProperties; } } /** * Lookup {@link Properties} for a specified qualifier * @param qualifier qualifier to be used during startup * @return {@link Properties} for a qualifier or null */ public static Properties retrieveProperties(String qualifier) { String identification = qualifier+".properties"; URL propertiesURL = STACK_LOOKUPER.lookup(identification); if(propertiesURL==null) return null; Properties ret = new Properties(); try { ret.load(propertiesURL.openStream()); LOG.info("Startup properties was loaded from '"+propertiesURL+"' for qualifier '"+qualifier+"'"); return ret; } catch (IOException e) { LOG.error("Can't read from properties file '"+propertiesURL+"' for qualifier '"+qualifier+"'", e); return null; } } private static Properties retrieveSystemProperties(Properties loadedProperties) { //Try to load from OS env Map<String, String> osProperties = System.getenv(); for(Map.Entry<String, String> entry : osProperties.entrySet()) { String env = entry.getKey().replace('_', '.'); if(loadedProperties.containsKey(env)) loadedProperties.setProperty(env, entry.getValue()); else { env = env.toLowerCase(); if(loadedProperties.containsKey(env)) loadedProperties.setProperty(env, entry.getValue()); } } //Load from Java system properties loadedProperties.putAll(System.getProperties()); return loadedProperties; } public Module loadFromClasspath(Module... initModules) { List<Module> allModules = new LinkedList<Module>(); List<Module> runtime = new LinkedList<Module>(); List<Module> overrides = new LinkedList<Module>(); if(initModules!=null && initModules.length>0) allModules.addAll(Arrays.asList(initModules)); Iterables.addAll(allModules, ServiceLoader.load(Module.class)); for (Module module : allModules) { if (module.getClass().isAnnotationPresent(OverrideModule.class)) overrides.add(module); else runtime.add(module); } return overrides.isEmpty() ? Modules.combine(runtime) : Modules.override(runtime).with(overrides); } }