org.atomserver.utils.test.TestingAtomServer.java Source code

Java tutorial

Introduction

Here is the source code for org.atomserver.utils.test.TestingAtomServer.java

Source

/* Copyright (c) 2007 HomeAway, Inc.
 *  All rights reserved.  http://www.atomserver.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.atomserver.utils.test;

import org.apache.abdera.Abdera;
import org.apache.abdera.protocol.server.ServiceContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.atomserver.core.WorkspaceOptions;
import org.atomserver.core.autotaggers.XPathAutoTagger;
import org.atomserver.core.dbstore.DBBasedContentStorage;
import org.atomserver.core.dbstore.dao.impl.EntriesDAOiBatisImpl;
import org.atomserver.core.validators.RelaxNGValidator;
import org.atomserver.server.servlet.AtomServerServlet;
import org.atomserver.utils.hsql.HsqlBootstrapper;
import org.atomserver.utils.conf.ConfigurationAwareClassLoader;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;

import java.util.Collections;
import java.util.HashSet;
import java.util.Properties;

/**
 * TestingAtomServer - a tiny standalone AtomServer instance, for unit testing AtomServer clients.
 * <p/>
 * This class brings up an instance of AtomServer using an embedded Jetty server and an in-memory
 * HSQL DB.  It provides a simple API for configuring workspaces, or allows you to specify the
 * location on the classpath of a Spring XML file to configure any atomserver beans, including,
 * but not limited to, workspaces.
 * <p/>
 * usage:
 * <pre>
 *  TestingAtomServer server = new TestingAtomServer();
 *  server.addWorkspace("foo", true);           // add workspace "foo", which is localized
 *  server.start(9000, "atomserver", "v1");     // start at http://localhost:9000/atomserver/v1
 * <p/>
 *  // do your testing here
 * <p/>
 *  server.stop();
 * </pre>
 */
public class TestingAtomServer {
    private static final Log log = LogFactory.getLog(TestingAtomServer.class);

    private GenericWebApplicationContext appContext;

    private Server httpServer;
    private int port;

    private String springBeansLocation = null;
    private ManagedSet workspaceSet = new ManagedSet();

    public TestingAtomServer() {
        this(true);
    }

    public TestingAtomServer(boolean resetDB) {
        // reset this value, so that each time we NEW a TestingAtomServer, we get a new DB
        if (resetDB) {
            HsqlBootstrapper.setHasBootstrapped(false);
        }
    }

    public int start(String atomserverServletContext, String atomserverServletMapping) throws Exception {
        if (this.httpServer != null) {
            throw new IllegalStateException("Must stop server before calling start");
        }

        for (int i = 40000; i < 40010; i++) {
            try {
                this.httpServer = createServer(this.port = i, atomserverServletContext, atomserverServletMapping);
                this.httpServer.start();
                return this.port;
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("Couldn't start port on " + i + ", trying next port");
            }
        }

        throw new RuntimeException("Couldn't find open port");
    }

    public void start(int port, String atomserverServletContext, String atomserverServletMapping) throws Exception {
        if (this.httpServer != null) {
            throw new IllegalStateException("Must stop server before calling start");
        }

        this.httpServer = createServer(this.port = port, atomserverServletContext, atomserverServletMapping);
        this.httpServer.start();
    }

    public void stop() throws Exception {
        if (this.httpServer != null) {
            this.httpServer.stop();
            this.httpServer.join();
            this.httpServer = null;
            this.port = 0;
        }
    }

    public WebApplicationContext getAppContext() {
        return appContext;
    }

    /**
     * set the location on the classpath of a spring XML file to load.
     *
     * @param springBeansLocation the location on the classpath of a spring XML file
     */
    public void setSpringBeansLocation(String springBeansLocation) {
        this.springBeansLocation = springBeansLocation;
    }

    /**
     * A simple "chaining" API that lets the caller configure additional parameters of the workspaces.
     */
    public interface TestWorkspaceConfigurer {
        TestWorkspaceConfigurer setRncLocation(String rncLocation);

        TestWorkspaceConfigurer setXPathAutotaggerRules(String rules);

        TestWorkspaceConfigurer addPropertyValue(String name, Object value);
    }

    /**
     * add a new workspace to the testing server with the given name and localization flag.
     *
     * @param name      the name of the workspace
     * @param localized true iff the workspace is localized
     * @return a TestWorkspaceConfigurer to further configure the workspace
     */
    public TestWorkspaceConfigurer addWorkspace(String name, boolean localized) {
        // create a bean definition for the workspace and add it to the managed set.
        final RootBeanDefinition workspace = new RootBeanDefinition(WorkspaceOptions.class);

        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("name", name);
        propertyValues.addPropertyValue("defaultLocalized", localized);
        propertyValues.addPropertyValue("defaultProducingEntryCategoriesFeedElement", true);
        propertyValues.addPropertyValue("defaultContentStorage",
                new RuntimeBeanReference("org.atomserver-contentStorage"));
        propertyValues.addPropertyValue("defaultContentValidator",
                new RuntimeBeanReference("org.atomserver-simpleXMLContentValidator"));
        propertyValues.addPropertyValue("defaultCategoriesHandler",
                new RuntimeBeanReference("org.atomserver-entryCategoriesHandler"));
        propertyValues.addPropertyValue("defaultEntryIdGenerator",
                new RuntimeBeanReference("org.atomserver-entryIdGenerator"));

        workspace.setPropertyValues(propertyValues);

        workspaceSet.add(workspace);

        return new TestWorkspaceConfigurer() {
            // if given a RNC location, we spin up a RelaxNGValidator to validate with it
            public TestWorkspaceConfigurer setRncLocation(String rncLocation) {
                RootBeanDefinition autotagger = new RootBeanDefinition(RelaxNGValidator.class);
                MutablePropertyValues propertyValues = new MutablePropertyValues();
                propertyValues.addPropertyValue("schemaLocation", rncLocation);
                autotagger.setPropertyValues(propertyValues);
                workspace.getPropertyValues().addPropertyValue("defaultContentValidator", autotagger);
                return this;
            }

            // if given an XPathAutoTagger script, set up a tagger to use it
            public TestWorkspaceConfigurer setXPathAutotaggerRules(String rules) {
                RootBeanDefinition autotagger = new RootBeanDefinition(XPathAutoTagger.class);
                MutablePropertyValues propertyValues = new MutablePropertyValues();
                propertyValues.addPropertyValue("categoriesHandler",
                        new RuntimeBeanReference("org.atomserver-entryCategoriesHandler"));
                propertyValues.addPropertyValue("script", rules);
                autotagger.setPropertyValues(propertyValues);
                workspace.getPropertyValues().addPropertyValue("defaultAutoTagger", autotagger);
                return this;
            }

            // allow for arbitrary property setting on the workspace bean -- requires knowledge
            // of the Spring bean factory APIs
            public TestWorkspaceConfigurer addPropertyValue(String name, Object value) {
                workspace.getPropertyValues().addPropertyValue(name, value);
                return this;
            }
        };
    }

    private Server createServer(int port, String atomserverServletContext, String atomserverServletMapping) {
        log.warn("TestingAtomServer.createServer() :: Argument atomserverServletMapping is no longer required, "
                + " and is deprecated");
        return createServer(port, atomserverServletContext);
    }

    private Server createServer(int port, String atomserverServletContext) {
        // set up the server and the atomserver web application context
        Server server = new Server(port);
        Context context = new Context(server, "/" + atomserverServletContext, true /*sessions*/,
                false /*no security*/);

        // we need to hard-code certain system properties to get the behavior we want here - we
        // will re-set them when we're done
        Properties properties = (Properties) System.getProperties().clone();
        System.setProperty("atomserver.env", "asdev-hsql-mem");
        System.setProperty("atomserver.servlet.context", atomserverServletContext);

        // TODO: this should be removed
        System.setProperty("atomserver.servlet.mapping", "v1");

        // our Spring application context will start off by loading the basic built-in bean
        // definitions
        appContext = new GenericWebApplicationContext();

        XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(appContext);
        xmlReader.loadBeanDefinitions(new ClassPathResource("org/atomserver/spring/propertyConfigurerBeans.xml"));
        xmlReader.loadBeanDefinitions(new ClassPathResource("org/atomserver/spring/databaseBeans.xml"));
        xmlReader.loadBeanDefinitions(new ClassPathResource("org/atomserver/spring/logBeans.xml"));
        xmlReader.loadBeanDefinitions(new ClassPathResource("org/atomserver/spring/storageBeans.xml"));
        xmlReader.loadBeanDefinitions(new ClassPathResource("org/atomserver/spring/abderaBeans.xml"));

        // if we were given a Spring config location, we use that -- otherwise we configure the
        // workspaces that were set up through the API
        if (springBeansLocation != null) {
            xmlReader.loadBeanDefinitions(new ClassPathResource(springBeansLocation));
        } else {
            RootBeanDefinition workspaces = new RootBeanDefinition(HashSet.class);
            ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
            constructorArgumentValues.addGenericArgumentValue(workspaceSet);
            workspaces.setConstructorArgumentValues(constructorArgumentValues);

            appContext.registerBeanDefinition("org.atomserver-workspaces", workspaces);
        }

        // override the base content storage to use DB-based storage
        RootBeanDefinition storage = new RootBeanDefinition(DBBasedContentStorage.class);
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("contentDAO", new RuntimeBeanReference("org.atomserver-contentDAO"));
        propertyValues.addPropertyValue("entriesDAO", new RuntimeBeanReference("org.atomserver-entriesDAO"));
        storage.setPropertyValues(propertyValues);
        appContext.registerBeanDefinition("org.atomserver-contentStorage", storage);

        // clear the existing ENV
        ConfigurationAwareClassLoader.invalidateENV();

        // refresh the context to actually instantiate everything.
        appContext.refresh();

        // re-set the system properties
        System.setProperties(properties);

        // clear the update ENV
        ConfigurationAwareClassLoader.invalidateENV();

        // put our app context into the servlet context
        context.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, appContext);

        // load and init the service context for v1
        final ServiceContext serviceContext = (ServiceContext) appContext.getBean(ServiceContext.class.getName());
        serviceContext.init(new Abdera(), Collections.EMPTY_MAP);

        // create a new AtomServerServlet - but override the createServiceContext method
        AtomServerServlet servlet = new AtomServerServlet() {
            protected ServiceContext createServiceContext() {
                return serviceContext;
            }
        };

        // load and init the service context for v2
        final ServiceContext serviceContextV2 = (ServiceContext) appContext
                .getBean("org.atomserver-serviceContext.v2");
        serviceContextV2.init(new Abdera(), Collections.EMPTY_MAP);

        // create a new AtomServerServlet - but override the createServiceContext method
        AtomServerServlet servletV2 = new AtomServerServlet() {
            protected ServiceContext createServiceContext() {
                return serviceContextV2;
            }
        };

        // register the servlets
        context.addServlet(new ServletHolder(servlet), "/v1/*");
        context.addServlet(new ServletHolder(servletV2), "/v2/*");

        EntriesDAOiBatisImpl entriesDAO = (EntriesDAOiBatisImpl) appContext.getBean("org.atomserver-entriesDAO");
        entriesDAO.setUseWorkspaceCollectionCache(false);

        // ready to be started
        return server;
    }
}