package org.atomhopper; import org.apache.abdera.Abdera; import org.apache.abdera.ext.json.JSONFilter; import org.apache.abdera.protocol.server.Filter; import org.apache.abdera.protocol.server.Provider; import org.apache.abdera.protocol.server.servlet.AbderaServlet; import org.apache.commons.lang.StringUtils; import org.atomhopper.abdera.WorkspaceProvider; import org.atomhopper.config.AtomHopperConfigurationPreprocessor; import org.atomhopper.config.WorkspaceConfigProcessor; import org.atomhopper.config.v1_0.*; import org.atomhopper.exceptions.ContextAdapterResolutionException; import org.atomhopper.exceptions.ServletInitException; import org.atomhopper.servlet.ApplicationContextAdapter; import org.atomhopper.servlet.DefaultEmptyContext; import org.atomhopper.servlet.ServletInitParameter; import org.atomhopper.util.config.ConfigurationParser; import org.atomhopper.util.config.ConfigurationParserException; import org.atomhopper.util.config.jaxb.JAXBConfigurationParser; import org.atomhopper.util.config.resource.file.FileConfigurationResource; import org.atomhopper.util.config.resource.uri.URIConfigurationResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; /** * This class is the entry point for the atom server application. This servlet is * responsible for setting up any required services as well as performing the * parsing of the atom server configuration. In addition, the servlet is also * responsible for context clean-up using the destroy method. This method should * make sure that any resources that have independent thread life-cycles are correctly * disposed of. */ public final class AtomHopperServlet extends AbderaServlet { private static final Logger LOG = LoggerFactory.getLogger(AtomHopperServlet.class); private static final String DEFAULT_CONFIGURATION_LOCATION = "/etc/atomhopper/atom-server.cfg.xml"; private final ConfigurationParser<Configuration> configurationParser; private ApplicationContextAdapter applicationContextAdapter; private Abdera abderaReference; private Configuration configuration; public AtomHopperServlet() { //TODO: One day I'm going to integrate Power API's configuration framework into this but until this, this'll do configurationParser = new JAXBConfigurationParser<Configuration>(Configuration.class, org.atomhopper.config.v1_0.ObjectFactory.class); } @Override public void init() throws ServletException { abderaReference = getAbdera(); final String configLocation = getConfigurationLocation(); LOG.info("Reading configuration: " + configLocation); try { try { configurationParser.setConfigurationResource(new URIConfigurationResource(new URI(configLocation))); } catch (URISyntaxException ex) { configurationParser.setConfigurationResource(new FileConfigurationResource(configLocation)); } configuration = configurationParser.read(); } catch (ConfigurationParserException cpe) { LOG.error("Failed to read configuration file: " + configLocation, cpe); throw new ServletInitException(cpe.getMessage(), cpe); } applicationContextAdapter = getContextAdapter(); applicationContextAdapter.usingServletContext(getServletContext()); super.init(); } protected ApplicationContextAdapter getContextAdapter() throws ContextAdapterResolutionException { String adapterClass = getInitParameter(ServletInitParameter.CONTEXT_ADAPTER_CLASS.toString()); // If no adapter class is set then use the default empty one if (StringUtils.isBlank(adapterClass)) { adapterClass = DefaultEmptyContext.class.getName(); } try { final Object freshAdapter = Class.forName(adapterClass).newInstance(); if (freshAdapter instanceof ApplicationContextAdapter) { return (ApplicationContextAdapter) freshAdapter; } } catch (Exception ex) { LOG.error(ex.getMessage(), ex); throw new ContextAdapterResolutionException(ex.getMessage(), ex); } throw new ContextAdapterResolutionException("Unknown application context adapter class: " + adapterClass); } protected String getConfigurationLocation() { final String configLocation = getInitParameter(ServletInitParameter.CONFIGURATION_LOCATION.toString()); return !StringUtils.isBlank(configLocation) ? configLocation : DEFAULT_CONFIGURATION_LOCATION; } @Override protected Provider createProvider() { final WorkspaceProvider workspaceProvider = new WorkspaceProvider(getHostConfiguration()); final String atomhopperUrlPattern = (getServletConfig().getInitParameter("atomhopper-url-pattern") == null) ? "/" : getServletConfig().getInitParameter("atomhopper-url-pattern"); workspaceProvider.init(abderaReference, parseDefaults(configuration.getDefaults())); final AtomHopperConfigurationPreprocessor preprocessor = new AtomHopperConfigurationPreprocessor(configuration); configuration = preprocessor.applyDefaults().getConfiguration(); ConfigurationDefaults configurationDefaults = configuration.getDefaults(); workspaceProvider.init(abderaReference, parseDefaults(configurationDefaults)); for (WorkspaceConfiguration workspaceCfg : configuration.getWorkspace()) { final WorkspaceConfigProcessor cfgProcessor = new WorkspaceConfigProcessor( workspaceCfg, applicationContextAdapter, workspaceProvider.getTargetResolver(), atomhopperUrlPattern, getHostConfiguration() ); workspaceProvider.getWorkspaceManager().addWorkspaces(cfgProcessor.toHandler()); } // adding the workspace provider filters if ( configuration.getProviderFilters() != null ) { for (FilterDescriptor filterD : configuration.getProviderFilters().getProviderFilter() ) { String beanId = filterD.getReference(); Filter aFilter = applicationContextAdapter.fromContext(beanId, Filter.class); workspaceProvider.addFilter(aFilter); } } return workspaceProvider; } private HostConfiguration getHostConfiguration() { //Initial parsing validation rules specify that there must always be a host configuration final HostConfiguration hostConfiguration = configuration.getHost(); if (StringUtils.isBlank(hostConfiguration.getDomain())) { throw new ConfigurationParserException("No domain specified in the host configuration. This is required for link generation. Halting."); } return hostConfiguration; } private Map<String, String> parseDefaults(ConfigurationDefaults defaults) { final Map<String, String> parameterMap = new HashMap<String, String>(); if (defaults != null && defaults.getAuthor() != null && !StringUtils.isBlank(defaults.getAuthor().getName())) { parameterMap.put("author", defaults.getAuthor().getName()); } return parameterMap; } }