001 /*
002 * Databinder: a simple bridge from Wicket to Hibernate
003 * Copyright (C) 2007 Nathan Hamblen nathan@technically.us
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
018 */
019 package net.databinder.web;
020
021 import java.io.File;
022 import java.net.URL;
023 import java.net.URLClassLoader;
024 import java.util.ArrayList;
025 import java.util.List;
026 import java.util.regex.Matcher;
027 import java.util.regex.Pattern;
028
029 import org.apache.wicket.util.string.Strings;
030 import org.mortbay.jetty.Connector;
031 import org.mortbay.jetty.Server;
032 import org.mortbay.jetty.ajp.Ajp13SocketConnector;
033 import org.mortbay.jetty.handler.MovedContextHandler;
034 import org.mortbay.jetty.nio.SelectChannelConnector;
035 import org.mortbay.jetty.webapp.WebAppContext;
036 import org.slf4j.Logger;
037 import org.slf4j.LoggerFactory;
038
039 /**
040 * Optional starter class for embedded Jetty. Client applications may pass
041 * this classname to the Java runtime to serve with no other configuration. The webroot
042 * defaults to src/main/webapp, and the server to a context named after the project directory
043 * with HTTP on port 8080. <tt>jetty.warpath</tt>, <tt>jetty.contextpath</tt>,
044 * <tt>jetty.port</tt>, and <tt>jetty.ajp.port</tt> system properties
045 * may be used to override (e.g. <tt>-Djetty.port=80</tt> as a command line parameter). AJP
046 * is enabled by specifying a port, and HTTP disabled by setting jetty.port to 0.
047 *
048 * <p>Some customization can be accomplished by extending this class and overriding
049 * the {@link DataServer#configure(Server, WebAppContext)} method. The subclass
050 * will need a static main(args) method that constructs an instance of itself, similar
051 * to {@link DataServer#main(String[])}
052 * @author Nathan Hamblen
053 */
054 public class DataServer {
055 private static final Logger log = LoggerFactory.getLogger(DataServer.class);
056
057 /** Constructs DataServer, kicking off server. */
058 public static void main(String[] args)
059 {
060 new DataServer();
061 }
062
063 /** Starts web sever using any parameters supplied. Calls
064 * {DataServer{@link #configure(Server, WebAppContext)} immediately before
065 * starting server.
066 */
067 public DataServer() {
068 try {
069 Server server = new Server();
070
071 WebAppContext web = new WebAppContext();
072
073 URL classes = null;
074 String projectDir = null;
075 try {
076 // look for project's classes directory
077 URL[] urls = ((URLClassLoader)DataServer.class.getClassLoader()).getURLs();
078 for (URL url : urls)
079 if (url.getPath().endsWith("classes/")) {
080 classes = url;
081 break;
082 }
083 } catch (Exception e) {
084 log.info("unable to find project path by classloader", e);
085 }
086
087 if (classes == null) {
088 projectDir = new File(".").getCanonicalPath();
089 log.info("project path fram current directory: " + projectDir);
090 } else {
091 projectDir = classes.toURI().resolve("../..").getPath();
092 log.info("project path as found by classloader: " + projectDir);
093 }
094
095 String contextPath = System.getProperty("jetty.contextpath",
096 System.getProperty("jetty.contextPath"));
097 if (Strings.isEmpty(contextPath)) {
098 Matcher m = Pattern.compile("(\\/[^\\/]+)/?$").matcher(projectDir);
099 if (!m.find())
100 throw new RuntimeException("Project path not as expected: " + projectDir);
101 contextPath = m.group(1);
102 log.info("context path by project directory: " + contextPath);
103 }
104 else
105 log.info("jetty.contextPath property: " + contextPath);
106 web.setContextPath(contextPath);
107
108 String warPath = System.getProperty("jetty.warpath",
109 System.getProperty("jetty.warPath"));
110 if (Strings.isEmpty(warPath)) warPath = projectDir + "src/main/webapp";
111
112 if (!new File(warPath).isDirectory()) {
113 log.error("Unable to find webapps path: " + warPath +
114 " \nPlease ensure that this project is the first on its " +
115 "classpath, or set a valid jetty.warpath JVM property.");
116 return;
117 }
118
119
120 else log.info("jetty.warPath property: " + warPath);
121 web.setWar(warPath);
122
123 server.addHandler(web);
124
125 if (!contextPath.equals("/"))
126 server.addHandler(new MovedContextHandler(server, "/", contextPath));
127
128 List<Connector> conns = new ArrayList<Connector>(2);
129
130 int httpPort = 8080;
131 try {
132 httpPort = Integer.valueOf(System.getProperty("jetty.port"));
133 log.info("jetty.port property: " + httpPort);
134 } catch (NumberFormatException e) { }
135
136 if (httpPort != 0) {
137 SelectChannelConnector httpConn = new SelectChannelConnector();
138 httpConn.setPort(httpPort);
139 conns.add(httpConn);
140 }
141
142 int ajpPort = 0;
143 try {
144 ajpPort = Integer.valueOf(System.getProperty("jetty.ajp.port"));
145 log.info("jetty.ajp.port property: " + ajpPort);
146 } catch (NumberFormatException e) { }
147
148 if (ajpPort != 0) {
149 Ajp13SocketConnector ajpConn = new Ajp13SocketConnector();
150 ajpConn.setPort(ajpPort);
151 conns.add(ajpConn);
152 }
153
154 server.setConnectors(conns.toArray(new Connector[conns.size()]));
155 server.setStopAtShutdown(true);
156
157 configure(server, web);
158
159 server.start();
160 if (httpPort != 0)
161 log.info("Ready at http://localhost:" + httpPort + contextPath);
162 if (ajpPort != 0)
163 log.info("Ready at ajp://localhost:" + ajpPort + contextPath);
164 server.join();
165
166 stopped(server, web);
167 } catch (Exception e) {
168 throw new RuntimeException(e);
169 }
170 }
171
172 /**
173 * Override to customize the server and context objects.
174 * @see DataServer#DataServer()
175 */
176 protected void configure(Server server, WebAppContext context) throws Exception { }
177
178 /**
179 * Override to perform action after server stops
180 * @see DataServer#DataServer()
181 */
182 protected void stopped(Server server, WebAppContext context) throws Exception { }
183 }