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.lifecycles;
017
018 import java.net.BindException;
019 import java.util.HashMap;
020 import java.util.Map;
021 import java.util.Set;
022
023 import org.apache.log4j.Logger;
024 import org.kuali.rice.core.api.config.property.Config;
025 import org.kuali.rice.core.api.config.property.ConfigContext;
026 import org.kuali.rice.core.api.lifecycle.Lifecycle;
027 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
028 import org.kuali.rice.core.api.resourceloader.ResourceLoader;
029 import org.kuali.rice.core.api.util.RiceUtilities;
030 import org.kuali.rice.core.web.jetty.JettyServer;
031 import org.mortbay.jetty.webapp.WebAppClassLoader;
032
033
034 /**
035 * A lifecycle for running a jetty web server.
036 * @author Kuali Rice Team (rice.collab@kuali.org)
037 */
038 public class JettyServerLifecycle implements Lifecycle {
039 private static final Logger LOG = Logger.getLogger(JettyServerLifecycle.class);
040
041 private static final HashMap<Integer, Config> WEBAPP_CONFIGS = new HashMap<Integer, Config>();
042
043 public static Config getWebappConfig(int port) {
044 return WEBAPP_CONFIGS.get(port);
045 }
046
047 /**
048 * Enum for dealing with the webapp's Config
049 */
050 public static enum ConfigMode {
051 /**
052 * Do nothing
053 */
054 NONE,
055 /**
056 * Override the Config for the context class loader
057 */
058 OVERRIDE,
059 /**
060 * Merge the webapp's Config into the existing context class loader config
061 */
062 MERGE
063 }
064
065 /**
066 * By default we set the JettyServer to test mode
067 */
068 private boolean testMode = true;
069 private boolean started;
070 private ConfigMode configMode = ConfigMode.OVERRIDE;
071 private boolean addWebappResourceLoaders = true;
072
073
074 protected JettyServer jettyServer;
075
076 public JettyServerLifecycle() {
077 this(8080, null);
078 }
079
080 public JettyServerLifecycle(int port) {
081 this(port, null, null);
082 }
083
084 public JettyServerLifecycle(int port, String contextName) {
085 this(port, contextName, null);
086 }
087
088 public JettyServerLifecycle(int port, String contextName, String relativeWebappRoot) {
089 jettyServer = new JettyServer(port, contextName, relativeWebappRoot);
090 jettyServer.setFailOnContextFailure(true);
091 jettyServer.setTestMode(testMode);
092 }
093
094 public void setTestMode(boolean t) {
095 this.testMode = t;
096 }
097
098 public boolean isTestMode() {
099 return testMode;
100 }
101
102 public ConfigMode getConfigMode() {
103 return this.configMode;
104 }
105
106 public void setConfigMode(ConfigMode configMode) {
107 this.configMode = configMode;
108 }
109
110 public boolean isAddWebappResourceLoaders() {
111 return this.addWebappResourceLoaders;
112 }
113
114 public void setAddWebappResourceLoaders(boolean addWebappResourceLoaders) {
115 this.addWebappResourceLoaders = addWebappResourceLoaders;
116 }
117
118 public boolean isStarted() {
119 return started;
120 }
121
122 public void start() throws Exception {
123 try {
124 jettyServer.start();
125
126 } catch (RuntimeException re) {
127 // add some handling to make port conflicts more easily identified
128 if (RiceUtilities.findExceptionInStack(re, BindException.class) != null) {
129 LOG.error("JettyServerLifecycle encountered BindException on port: " + jettyServer.getPort() + "; check logs for test failures or and the config for duplicate port specifications.");
130 }
131 throw re;
132 }
133
134 ClassLoader webappClassLoader = jettyServer.getContext().getClassLoader();
135 if (addWebappResourceLoaders) {
136 ResourceLoader rl = GlobalResourceLoader.getResourceLoader(webappClassLoader);
137 if (rl == null) {
138 throw new RuntimeException("Could not find resource loader for workflow test harness web app for: " + webappClassLoader);
139 }
140 GlobalResourceLoader.addResourceLoader(rl);
141 }
142
143 // TODO: RICE-2.0 UPGRADE had to jump through hoops with M8 because getConfig was made private. Uncomment the following and remove the next block
144 // Config webappConfig = ConfigContext.getConfig(webappClassLoader);
145
146 Config webappConfig = null;
147 for (Map.Entry<ClassLoader, Config> configEntry : ConfigContext.getConfigs()) {
148 if (configEntry.getKey() instanceof WebAppClassLoader) {
149 webappConfig = configEntry.getValue();
150 }
151 }
152
153 WEBAPP_CONFIGS.put(jettyServer.getPort(), webappConfig);
154 if (ConfigMode.OVERRIDE == configMode) {
155 // this overrides the test harness classloader config with the webapp's config...
156 ConfigContext.overrideConfig(Thread.currentThread().getContextClassLoader(), webappConfig);
157 } else if (ConfigMode.MERGE == configMode) {
158 Config curCtxConfig = ConfigContext.getCurrentContextConfig();
159 if (webappConfig != null) {
160 curCtxConfig.putProperties(webappConfig.getProperties());
161 curCtxConfig.putObjects(webappConfig.getObjects());
162 }
163 }
164
165 started = true;
166 }
167
168 public void stop() throws Exception {
169 LOG.info("Shutting down jetty: " + jettyServer);
170 try {
171 if (jettyServer != null && jettyServer.isStarted()) {
172 jettyServer.stop();
173 WEBAPP_CONFIGS.remove(jettyServer.getPort());
174 }
175 } catch (Exception e) {
176 LOG.error("Error shutting down Jetty " + jettyServer.getContextName() + " " + jettyServer.getRelativeWebappRoot(), e);
177 }
178 started = false;
179 }
180 }