001    /*
002     * Copyright 2007-2008 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.test;
017    
018    import java.lang.reflect.Method;
019    import java.util.ArrayList;
020    import java.util.Arrays;
021    import java.util.HashSet;
022    import java.util.List;
023    import java.util.Set;
024    
025    import org.apache.commons.lang.StringUtils;
026    import org.kuali.rice.core.api.config.property.ConfigContext;
027    import org.kuali.rice.core.api.lifecycle.Lifecycle;
028    import org.kuali.rice.test.lifecycles.JettyServerLifecycle;
029    import org.kuali.rice.test.server.JettyServer;
030    import org.kuali.rice.test.server.JettyServers;
031    
032    /**
033     * A test case that supports declaratively defining a JettServer to run via annotations.
034     * Although this class supports running JettyServers on a per-suite and per-test basis, there is a
035     * pragmatic issue of concurrently running webapp contexts in the same classloader, so the webapp
036     * will have to support this (e.g. not rely on static singletons...)
037     * Another issue is that there is no suite "shutdown" per se, so a Jetty started up in a suite
038     * lifecycle will never get explicitly shut down.
039     * @author Kuali Rice Team (rice.collab@kuali.org)
040     */
041    public abstract class JettyServerTestCase extends BaselineTestCase {
042        private static Set<String> classesHandled = new HashSet<String>();
043        
044        /**
045         * @see {@link BaselineTestCase#BaselineTestCase(String, Mode)
046         */
047        public JettyServerTestCase(String moduleName, Mode mode) {
048            super(moduleName, mode);
049        }
050    
051        /**
052         * @see {@link BaselineTestCase#BaselineTestCase(String)
053         */
054        public JettyServerTestCase(String moduleName) {
055            super(moduleName);
056        }
057    
058        private List<JettyServer> readPerTestDefinitions(Method method) {
059            return getJettyServerDefinitions((JettyServers) method.getAnnotation(JettyServers.class), (JettyServer) method.getAnnotation(JettyServer.class));
060        }
061    
062        private List<JettyServer> readSuiteServerDefinitions(Class clazz) {
063            return getJettyServerDefinitions((JettyServers) clazz.getAnnotation(JettyServers.class), (JettyServer) clazz.getAnnotation(JettyServer.class));
064        }
065    
066        private List<JettyServer> getJettyServerDefinitions(JettyServers servers, JettyServer server) {
067            List<JettyServer> defs = new ArrayList<JettyServer>();
068            if (servers != null) {
069                if (servers.servers() != null) {
070                    defs.addAll(Arrays.asList(servers.servers()));
071                }
072            }
073            if (server != null) {
074                defs.add(server);
075            }
076            return defs;
077        }
078    
079        protected JettyServerLifecycle constructJettyServerLifecycle(JettyServer def) {
080            String portConfigParam = def.portConfigParam();
081            if (StringUtils.isBlank(portConfigParam)) {
082                portConfigParam = null;
083            }
084            if (def.port() != JettyServer.CONFIG_PARAM_PORT && portConfigParam != null) {
085                throw new IllegalArgumentException("Either but not both of port or portConfigParam must be specified on JettyServer annotation");
086            }
087            if (def.port() == JettyServer.CONFIG_PARAM_PORT && portConfigParam == null) {
088                portConfigParam = "unittest.jetty." + def.context() + ".port";
089            }
090            JettyServerLifecycle lc;
091            if (def.port() != JettyServer.CONFIG_PARAM_PORT) {
092                lc = new RuntimePortJettyServerLifecycle(def.port(), "/" + def.context(), def.webapp());
093            } else {
094                lc = new RuntimePortJettyServerLifecycle(def.portConfigParam(), "/" + def.context(), def.webapp());
095            }
096            lc.setConfigMode(def.configMode());
097            lc.setAddWebappResourceLoaders(def.addWebappResourceLoader());
098            return lc;
099        }
100    
101        @Override
102        protected List<Lifecycle> getSuiteLifecycles() {
103            List<Lifecycle> lifecycles = super.getSuiteLifecycles();
104            List<Class> classesToHandle;
105            try {
106                classesToHandle = TestUtilities.getHierarchyClassesToHandle(this.getClass(), new Class[] { JettyServers.class, JettyServer.class }, classesHandled);
107            } catch (Exception e) {
108                throw new RuntimeException("Error determining classes to handle", e);
109            }
110            List<JettyServer> suiteDefs = new ArrayList<JettyServer>();
111            for (Class c: classesToHandle) {
112                suiteDefs.addAll(readSuiteServerDefinitions(c));
113                classesHandled.add(c.getName());
114            }
115            // add all our jetties...
116            for (JettyServer def: suiteDefs) {
117                lifecycles.add(constructJettyServerLifecycle(def));
118            }
119            return lifecycles;
120        }
121    
122        @Override
123        protected List<Lifecycle> getPerTestLifecycles() {
124            List<Lifecycle> lifecycles = super.getPerTestLifecycles();
125            List<JettyServer> defs = readPerTestDefinitions(method);
126            // add all our jetties...
127            for (JettyServer def: defs) {
128                lifecycles.add(constructJettyServerLifecycle(def));
129            }
130            return lifecycles;
131        }
132        
133        private static final class RuntimePortJettyServerLifecycle extends JettyServerLifecycle {
134            private String portConfigParam;
135            public RuntimePortJettyServerLifecycle(int port, String contextName, String relativeWebappRoot) {
136                super(port, contextName, relativeWebappRoot);
137            }
138            public RuntimePortJettyServerLifecycle(String portConfigParam, String contextName, String relativeWebappRoot) {
139                super(JettyServer.CONFIG_PARAM_PORT, contextName, relativeWebappRoot);
140                this.portConfigParam = portConfigParam;
141            }
142            public void start() throws Exception {
143                if (jettyServer.getPort() == JettyServer.CONFIG_PARAM_PORT) {
144                    String val = ConfigContext.getCurrentContextConfig().getProperty(portConfigParam);
145                    if (val == null) {
146                        throw new RuntimeException("Jetty port not found in config param: " + portConfigParam);
147                    }
148                    jettyServer.setPort(Integer.parseInt(val));
149                }
150                super.start();
151            }
152            @Override
153            public void stop() throws Exception {
154                System.err.println("Shutting down jetty");
155                super.stop();
156            }
157        }
158    }