View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.core.web.jetty;
17  
18  import java.io.File;
19  import java.lang.reflect.Field;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.commons.lang.builder.ToStringBuilder;
23  import org.kuali.rice.core.api.lifecycle.Lifecycle;
24  import org.mortbay.jetty.Server;
25  import org.mortbay.jetty.servlet.Context;
26  import org.mortbay.jetty.servlet.ServletHolder;
27  import org.mortbay.jetty.webapp.WebAppContext;
28  
29  public class JettyServer implements Lifecycle {
30  	
31  	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
32  			.getLogger(JettyServer.class);
33  	
34      /**
35       * The name of an attribute we set in the ServletContext to indicate to the webapp
36       * that it is running within unit tests, in case it needs to alter its configuration
37       * or behavior.
38       */
39      public static final String JETTYSERVER_TESTMODE_ATTRIB = "JETTYSERVER_TESTMODE";
40  
41  	private int port;
42  	private String contextName;	
43  	private String relativeWebappRoot;
44  	private Class servletClass;
45  	private Server server;
46  	private Context context;
47  	private boolean failOnContextFailure;
48  
49  	/**
50  	 * Whether we are in test mode
51  	 */
52  	private boolean testMode = false;
53  
54  	public JettyServer() {
55  		this(8080);
56  	}
57  
58  	public JettyServer(int port) {
59  		this(port, null, null, null);
60  	}
61  
62  	public JettyServer(int port, String contextName) {
63  		this(port, contextName, null, null);
64  	}
65  	
66  	public JettyServer(int port, String contextName, String relativeWebappRoot) {
67          this(port, contextName, relativeWebappRoot, null);
68  	}	
69  
70      public JettyServer(int port, String contextName, Class servletClass) {
71          this(port, contextName, null, servletClass);
72      }   
73  
74      public JettyServer(int port, String contextName, String relativeWebappRoot, Class servletClass) {
75          this.port = port;
76          this.contextName = contextName;
77          this.relativeWebappRoot = relativeWebappRoot;
78          this.servletClass = servletClass;
79      }   
80  
81      public void setTestMode(boolean t) {
82  	    this.testMode = t;
83  	}
84  
85  	public boolean isTestMode() {
86  	    return testMode;
87  	}
88  
89  	public Server getServer() {
90  		return server;
91  	}
92  
93  	public Context getContext() {
94  	    return context;
95  	}
96  
97  	public void start() throws Exception {
98  		server = createServer();
99  		server.start();
100 		if (isFailOnContextFailure() && contextStartupFailed()) {
101 			try {
102 				server.stop();
103 			} catch (Exception e) {
104 				LOG.warn("Failed to stop server after web application startup failure.");
105 			}
106 			throw new Exception("Failed to startup web application context!  Check logs for specific error.");
107 		}
108 	}
109 
110 	public void stop() throws Exception {
111 		server.stop(); 
112 	}
113 
114 	public boolean isStarted() {
115 		return server.isStarted();
116 	}
117 
118 	protected Server createServer() {
119 		Server server = new Server(getPort());
120 		setBaseDirSystemProperty();
121 		if (useWebAppContext()) {
122 			File tmpDir = new File(System.getProperty("basedir") + "/target/jetty-tmp");
123 			tmpDir.mkdirs();
124 			if (LOG.isInfoEnabled()) {
125 				LOG.info("WebAppRoot = " + System.getProperty("basedir") + getRelativeWebappRoot());
126 			}
127 			WebAppContext webAppContext = new WebAppContext(System.getProperty("basedir") + getRelativeWebappRoot(), getContextName());
128 			webAppContext.setTempDirectory(tmpDir);
129 			webAppContext.setAttribute(JETTYSERVER_TESTMODE_ATTRIB, String.valueOf(isTestMode()));
130 			context = webAppContext;
131 			server.addHandler(context);
132 		} else {
133 			Context root = new Context(server,"/",Context.SESSIONS);
134 			root.addServlet(new ServletHolder(servletClass), getContextName());
135 			root.setAttribute(JETTYSERVER_TESTMODE_ATTRIB, String.valueOf(isTestMode()));
136 			context = root;
137 		}
138 		return server;
139 	}
140 
141 	protected void setBaseDirSystemProperty() {
142 		if (System.getProperty("basedir") == null) {
143 			System.setProperty("basedir", System.getProperty("user.dir"));
144 		}
145 	}
146 	
147 	private boolean useWebAppContext() {
148 		return StringUtils.isNotBlank(this.relativeWebappRoot);
149 	}
150 	
151 	/**
152 	 * A hack for Jetty so that we can detect if context startup failed.  Jetty has no programatic
153 	 * way available to detect if context startup failed.  Instead we have to use reflection to
154 	 * check the value of a private variable.  See http://jira.codehaus.org/browse/JETTY-319
155 	 * for more details on the issue.
156 	 */
157 	protected boolean contextStartupFailed() throws Exception {
158         /*
159 		 * We can only tell if the context startup failed if the server is using a WebAppContext object since the
160 		 * org.mortbay.jetty.servlet.Context object does not have a field named '_unavailable'
161 		 */
162 		if (useWebAppContext()) {
163 			Field unavailableField = context.getClass().getDeclaredField("_unavailable");
164 			unavailableField.setAccessible(true);
165 			return unavailableField.getBoolean(context);
166 		}
167 		return false;
168 	}
169 	
170 	public String getRelativeWebappRoot() {
171 		if (relativeWebappRoot == null) {
172 			return "/sampleapp/web-root";
173 		}
174 		return relativeWebappRoot;
175 	}
176 
177 	public void setRelativeWebappRoot(String relativeWebappRoot) {
178 		this.relativeWebappRoot = relativeWebappRoot;
179 	}
180 
181 	public String getContextName() {
182 		if (contextName == null) {
183 			return "/SampleRiceClient";
184 		}
185 		return contextName;
186 	}
187 
188 	public void setContextName(String contextName) {
189 		this.contextName = contextName;
190 	}
191 
192 	public int getPort() {
193 		return port;
194 	}
195 
196 	public void setPort(int port) {
197 		this.port = port;
198 	}
199 	
200 	
201 	public boolean isFailOnContextFailure() {
202 		return this.failOnContextFailure;
203 	}
204 
205 	public void setFailOnContextFailure(boolean failOnContextFailure) {
206 		this.failOnContextFailure = failOnContextFailure;
207 	}
208 
209 	public String toString() {
210 	    return new ToStringBuilder(this).append("port", port)
211 	                                    .append("contextName", contextName)
212 	                                    .append("relativeWebappRoot", relativeWebappRoot)
213                                         .append("servletClass", servletClass)
214 	                                    .toString();
215 	}
216 
217     public static void main(String[] args) {
218         int port = args.length > 0 ? Integer.parseInt(args[0]) : 8080;
219         String contextName = args.length > 1 ? args[1] : null;
220         String relativeWebappRoot = args.length > 2 ? args[2] : null;
221         try {
222             new JettyServer(port, contextName, relativeWebappRoot).start();
223         } catch (Exception e) {
224             e.printStackTrace();
225         }
226     }
227 }