001: /* ====================================================================
002: The Jicarilla Software License
003:
004: Copyright (c) 2003 Leo Simons.
005: All rights reserved.
006:
007: Permission is hereby granted, free of charge, to any person obtaining
008: a copy of this software and associated documentation files (the
009: "Software"), to deal in the Software without restriction, including
010: without limitation the rights to use, copy, modify, merge, publish,
011: distribute, sublicense, and/or sell copies of the Software, and to
012: permit persons to whom the Software is furnished to do so, subject to
013: the following conditions:
014:
015: The above copyright notice and this permission notice shall be
016: included in all copies or substantial portions of the Software.
017:
018: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
019: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
020: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
021: IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
022: CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
023: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
024: SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
025: ==================================================================== */
026: package org.jicarilla.webserver;
027:
028: import EDU.oswego.cs.dl.util.concurrent.Executor;
029: import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
030: import org.apache.commons.cli.CommandLine;
031: import org.apache.commons.cli.HelpFormatter;
032: import org.apache.commons.cli.OptionBuilder;
033: import org.apache.commons.cli.Options;
034: import org.apache.commons.cli.PosixParser;
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.impl.SimpleLog;
037: import org.apache.commons.pool.impl.SoftReferenceObjectPool;
038: import org.jicarilla.container.Resolver;
039: import org.jicarilla.container.builder.Builder;
040: import org.jicarilla.container.builder.CustomComponent;
041: import org.jicarilla.container.builder.DefaultBuilder;
042: import org.jicarilla.container.selectors.ClassSelector;
043: import org.jicarilla.http.MessageFactory;
044: import org.jicarilla.lang.Active;
045: import org.jicarilla.lang.ExceptionListener;
046: import org.jicarilla.lang.LifecycleUtil;
047: import org.jicarilla.lang.NoopExceptionListener;
048: import org.jicarilla.lang.OrSelector;
049: import org.jicarilla.lang.Selector;
050: import org.jicarilla.net.EventFactory;
051: import org.jicarilla.net.SocketServer;
052: import org.jicarilla.net.SocketServerConfig;
053: import org.jicarilla.net.SocketServerImpl;
054: import org.jicarilla.plumbing.NoopSink;
055: import org.jicarilla.webserver.plumbing.BeanshellHTTPChannelFactory;
056: import org.jicarilla.webserver.plumbing.JettyChannelFactory;
057: import org.mortbay.http.HttpContext;
058: import org.mortbay.http.HttpServer;
059: import org.mortbay.http.handler.ResourceHandler;
060: import org.mortbay.util.MultiException;
061: import org.picocontainer.Parameter;
062: import org.picocontainer.defaults.CachingComponentAdapter;
063: import org.picocontainer.defaults.ComponentParameter;
064: import org.picocontainer.defaults.ConstantParameter;
065: import org.picocontainer.defaults.ConstructorComponentAdapter;
066:
067: import java.net.InetAddress;
068: import java.net.UnknownHostException;
069:
070: /**
071: * This version of main not only uses a Jicarilla Container instead of a
072: * PicoContainer, it also doesn't use BeanShell but hardwires things.
073: *
074: * @author <a href="lsimons at jicarilla dot org">Leo Simons</a>
075: * @version $Id: PureJavaMain.java,v 1.1 2004/03/31 12:11:00 lsimons Exp $
076: */
077: public class PureJavaMain implements Active {
078: public final static double MILLISECONDS_IN_A_SECOND = 1000.0;
079:
080: // default configuration
081: public final static int DEFAULT_PORT = 8080;
082: public final static int DEFAULT_BACKLOG = 500;
083: public final static String DEFAULT_ADDRESS = "127.0.0.1";
084: public final static int DEFAULT_THREADS = 5;
085: public final static int DEFAULT_LOG_LEVEL = SimpleLog.LOG_LEVEL_WARN;
086: public final static String DEFAULT_DIRECTORY = "/var/www/html";
087: //public final static Class DEFAULT_SERVER_CLASS =
088: // SocketServerImpl.class;
089: public final static int DEFAULT_INITIAL_POOL_SIZE = 20;
090:
091: // configuration
092: protected int m_port = DEFAULT_PORT;
093: protected int m_backlog = DEFAULT_BACKLOG;
094: protected String m_address = DEFAULT_ADDRESS;
095: protected int m_threads = DEFAULT_THREADS;
096: protected int m_logLevel = DEFAULT_LOG_LEVEL;
097: protected String m_directory = DEFAULT_DIRECTORY;
098: protected int m_initialPoolSize = DEFAULT_INITIAL_POOL_SIZE;
099: //protected Class m_serverClass;
100:
101: // helpers
102: protected Object m_config;
103: protected Log m_log;
104: protected Object m_server;
105: protected Resolver m_resolver;
106: protected String[] m_args;
107: protected CommandLine m_options;
108:
109: // instrumentation
110: protected long startTime = System.currentTimeMillis();
111:
112: protected PureJavaMain(final String[] args) throws Exception {
113: m_args = args;
114: setupCommandLine();
115: setupConfig();
116: setupLog();
117: setupContainer();
118: }
119:
120: public void initialize() throws Throwable {
121: m_server = (SocketServer) m_resolver.get(SocketServer.class);
122:
123: m_log.info("Starting Jicarilla server: " + m_address + ":"
124: + m_port);
125:
126: // todo: move to container!
127: LifecycleUtil.initialize(m_server);
128:
129: m_log.debug("Up in " + (System.currentTimeMillis() - startTime)
130: / MILLISECONDS_IN_A_SECOND + " seconds.");
131: }
132:
133: public void dispose() throws Throwable {
134: // todo: move to container!
135: LifecycleUtil.dispose(m_server);
136: m_resolver.releaseInstance(m_server);
137: }
138:
139: public static void main(final String[] args) {
140: Active main = null;
141: try {
142: main = new PureJavaMain(args);
143: main.initialize();
144: } catch (Throwable th) {
145: printError(
146: "Error initializing the server: ",
147: "Please try again. If the problem persists, contact your administrator.",
148: th, true);
149:
150: try {
151: if (main != null)
152: main.dispose();
153: } catch (Throwable t) {
154: printError("Error attempting a graceful shutdown: ",
155: "This is probably not a big problem.", t, true);
156: }
157: }
158: }
159:
160: // ----------------------------------------------------------------------
161: // Setup Helpers
162: // ----------------------------------------------------------------------
163: protected void setupCommandLine() {
164: final Options o = new Options();
165:
166: OptionBuilder.withArgName("string");
167: OptionBuilder.withLongOpt("base-directory");
168: OptionBuilder
169: .withDescription("the base directory from which to serve files");
170: OptionBuilder.withValueSeparator('=');
171: OptionBuilder.hasArg();
172: o.addOption(OptionBuilder.create('d'));
173:
174: OptionBuilder.withArgName("number");
175: OptionBuilder.withLongOpt("port");
176: OptionBuilder
177: .withDescription("the port on which to listen for requests");
178: OptionBuilder.withValueSeparator('=');
179: OptionBuilder.hasArg();
180: o.addOption(OptionBuilder.create('p'));
181:
182: OptionBuilder.withArgName("number");
183: OptionBuilder.withLongOpt("backlog");
184: OptionBuilder
185: .withDescription("the maximum size of the queue for the server socket");
186: OptionBuilder.withValueSeparator('=');
187: OptionBuilder.hasArg();
188: o.addOption(OptionBuilder.create('b'));
189:
190: OptionBuilder.withArgName("string");
191: OptionBuilder.withLongOpt("address");
192: OptionBuilder
193: .withDescription("the address on which to listen for requests");
194: OptionBuilder.withValueSeparator('=');
195: OptionBuilder.hasArg();
196: o.addOption(OptionBuilder.create('a'));
197:
198: OptionBuilder.withArgName("number");
199: OptionBuilder.withLongOpt("threads");
200: OptionBuilder
201: .withDescription("the number of base worker threads to create");
202: OptionBuilder.withValueSeparator('=');
203: OptionBuilder.hasArg();
204: o.addOption(OptionBuilder.create('t'));
205:
206: //OptionBuilder.withArgName( "string" );
207: //OptionBuilder.withLongOpt( "serverClass" );
208: //OptionBuilder.withDescription(
209: // "the class to use as the central active server" );
210: //OptionBuilder.withValueSeparator( '=' );
211: //OptionBuilder.hasArg();
212: //o.addOption( OptionBuilder.create( 'c' ) );
213:
214: OptionBuilder.withLongOpt("debug");
215: OptionBuilder.withDescription("be more verbose");
216: o.addOption(OptionBuilder.create('d'));
217:
218: OptionBuilder.withLongOpt("quiet");
219: OptionBuilder.withDescription("be less verbose");
220: OptionBuilder.withValueSeparator('=');
221: o.addOption(OptionBuilder.create('q'));
222:
223: //OptionBuilder.withArgName( "number" );
224: //OptionBuilder.withLongOpt( "bsh-port" );
225: //OptionBuilder.withDescription(
226: // "the port on which to run the telnet BeanShell server" );
227: //OptionBuilder.withValueSeparator( '=' );
228: //OptionBuilder.hasArg();
229: //o.addOption( OptionBuilder.create() );
230:
231: try {
232: final PosixParser parser = new PosixParser();
233: //parser = new GnuParser();
234: m_options = parser.parse(o, m_args);
235: } catch (Exception e) {
236: final HelpFormatter formatter = new HelpFormatter();
237: System.out.println(e.getMessage());
238: System.out
239: .println("--------------------------------------------------");
240: formatter
241: .printHelp(
242: "java org.jicarilla.webserver.Main [OPTIONS]",
243: "\nStart the Jicarilla Server. Available options are:",
244: o, "");
245: System.exit(1);
246: }
247: }
248:
249: protected void setupContainer() throws Exception {
250: final Builder builder = DefaultBuilder.newInstance()
251: .addComponent(m_log).addComponent(m_config);
252: m_resolver = builder.getResolver();
253:
254: // todo move jetty support elsewhere
255: //populate( builder );
256: populateWithJetty(builder);
257:
258: builder.create();
259: }
260:
261: protected void setupConfig() throws ClassNotFoundException {
262: if (m_options.hasOption("port"))
263: m_port = new Integer(m_options.getOptionValue("port"))
264: .intValue();
265:
266: m_backlog = m_options.hasOption("backlog") ? new Integer(
267: m_options.getOptionValue("backlog")).intValue()
268: : DEFAULT_BACKLOG;
269:
270: m_address = m_options.hasOption("address") ? m_options
271: .getOptionValue("address") : DEFAULT_ADDRESS;
272:
273: m_directory = m_options.hasOption("directory") ? m_options
274: .getOptionValue("directory") : DEFAULT_DIRECTORY;
275:
276: m_threads = m_options.hasOption("threads") ? new Integer(
277: m_options.getOptionValue("threads")).intValue()
278: : DEFAULT_THREADS;
279:
280: //m_serverClass = m_options.hasOption("server")?
281: // Class.forName( m_options.getOptionValue("server") ) :
282: // DEFAULT_SERVER_CLASS;
283:
284: m_logLevel = m_options.hasOption("quiet") ? SimpleLog.LOG_LEVEL_WARN
285: : SimpleLog.LOG_LEVEL_INFO;
286: if (m_options.hasOption("debug"))
287: m_logLevel = SimpleLog.LOG_LEVEL_DEBUG;
288:
289: m_config = new SocketServerConfig(m_address, m_port, m_backlog,
290: m_threads);
291: }
292:
293: protected void setupLog() {
294: final SimpleLog log = new SimpleLog(m_address);
295: log.setLevel(m_logLevel);
296: m_log = log;
297: }
298:
299: protected void populate(final Builder builder) {
300: builder.addComponent(ExceptionListener.class,
301: NoopExceptionListener.class);
302: builder.addComponent(new OrSelector(new Selector[] {
303: new ClassSelector(Executor.class),
304: new ClassSelector(PooledExecutor.class) }),
305: PooledExecutor.class);
306:
307: // HTTP component
308: /*builder.addComponent(
309: MessageReceivedListener.class,
310: NoopMessageReceivedListener.class
311: );*/
312: builder.addComponent("message-pool",
313: new SoftReferenceObjectPool(new MessageFactory()));
314: /*builder.addComponent(
315: new OrSelector(
316: new Selector[] {
317: new ClassSelector(HTTPHandler.class),
318: new ClassSelector(HTTPErrorHandler.class)
319: }
320: ),
321: new CachingComponentAdapter(
322: new ConstructorComponentAdapter(
323: HTTPHandler.class,
324: HTTPMessageGenerator.class,
325: new Parameter[] {
326: new ComponentParameter( ExceptionListener.class ),
327: new ComponentParameter( MessageReceivedListener.class ),
328: new ComponentParameter( "message-pool" )
329: }
330: )
331: )
332: );*/
333: /*builder.addComponent(
334: HTTPParser.class,
335: HTTPParserImpl.class
336: );*/
337:
338: // event pipeline
339: builder.addComponent("pipeline-factory",
340: new BeanshellHTTPChannelFactory());
341: builder
342: .addComponent(
343: "pipeline-pool",
344: new CachingComponentAdapter(
345: new ConstructorComponentAdapter(
346: "pipeline-pool",
347: SoftReferenceObjectPool.class,
348: new Parameter[] {
349: new ComponentParameter(
350: "pipeline-factory"),
351: new ConstantParameter(
352: new Integer(
353: m_initialPoolSize)) })));
354:
355: // Socket Server
356: builder.addComponent("event-pool", new SoftReferenceObjectPool(
357: new EventFactory()));
358: builder.addComponent("error-handler", new NoopSink());
359:
360: /*DefaultCustomizableResolver customResolver =
361: new DefaultCustomizableResolver( m_resolver )
362: .redirectCall( 3, "event-pool" )
363: .redirectCall( 4, "pipeline-pool" )
364: .redirectCall( 5, "error-handler" );
365: Type3Factory factory = new Type3Factory( customResolver,
366: SocketServerImpl.class.getName() );
367: SingletonAdapter adapter = new SingletonAdapter( factory );
368:
369: builder.addComponent(
370: SocketServer.class,
371: adapter
372: );*/
373:
374: builder.addComponent(SocketServer.class, new CustomComponent(
375: SocketServerImpl.class).redirectCall(3, "event-pool")
376: .redirectCall(4, "pipeline-pool").redirectCall(5,
377: "error-handler"));
378:
379: /*builder.addComponent(
380: SocketServer.class,
381: new CachingComponentAdapter(
382: new ConstructorComponentAdapter(
383: SocketServer.class,
384: SocketServerImpl.class,
385: new Parameter[] {
386: new ComponentParameter( SocketServerConfig.class ),
387: new ComponentParameter( ExceptionListener.class ),
388: new ComponentParameter( "event-pool" ),
389: new ComponentParameter( "pipeline-pool" ),
390: new ComponentParameter( "error-handler" ),
391: new ComponentParameter( PooledExecutor.class )
392: }
393: )
394: )
395: );*/
396: }
397:
398: // ----------------------------------------------------------------------
399: // Error Printing Helpers
400: // ----------------------------------------------------------------------
401: protected static void printError(final String prefix,
402: final String postfix, final Throwable t,
403: final boolean recurse) {
404: System.err.println(prefix + t.getMessage());
405: if (recurse) {
406: System.err.println("A stack trace follows:");
407: System.err
408: .println("-----------------------------------------------------");
409: t.printStackTrace();
410: //printRecursiveMessages( t.getCause(), 4 );
411: //if( t instanceof EvalError )
412: //{
413: // System.err.println( ((EvalError)t).getScriptStackTrace() );
414: //}
415: System.err
416: .println("-----------------------------------------------------");
417: }
418: System.err.println(postfix);
419: }
420:
421: protected static void printRecursiveMessages(final Throwable t,
422: final int indent) {
423: if (t == null)
424: return;
425:
426: for (int i = 0; i < indent; i++) {
427: System.err.print(' ');
428: }
429:
430: System.err.print(friendlyClassName(t) + ": " + t.getMessage());
431: System.err.println();
432:
433: printRecursiveMessages(t.getCause(), indent + 4);
434: }
435:
436: protected static String friendlyClassName(final Object o) {
437: final String fqn = o.getClass().getName();
438: final String last = fqn.substring(fqn.lastIndexOf('.'));
439: return last;
440: }
441:
442: // ----------------------------------------------------------------------
443: // Jetty Integration
444: // ----------------------------------------------------------------------
445:
446: protected void populateWithJetty(final Builder builder)
447: throws UnknownHostException, MultiException {
448: builder.addComponent(ExceptionListener.class,
449: NoopExceptionListener.class);
450: builder.addComponent(new OrSelector(new Selector[] {
451: new ClassSelector(Executor.class),
452: new ClassSelector(PooledExecutor.class) }),
453: PooledExecutor.class);
454: builder.addComponent("message-pool",
455: new SoftReferenceObjectPool(new MessageFactory()));
456:
457: // Jetty
458: builder.addComponent("pipeline-factory",
459: new JettyChannelFactory(createJettyServer(),
460: createInetAddress()));
461: builder
462: .addComponent(
463: "pipeline-pool",
464: new CachingComponentAdapter(
465: new ConstructorComponentAdapter(
466: "pipeline-pool",
467: SoftReferenceObjectPool.class,
468: new Parameter[] {
469: new ComponentParameter(
470: "pipeline-factory"),
471: new ConstantParameter(
472: new Integer(
473: m_initialPoolSize)) })));
474:
475: // Socket Server
476: builder.addComponent("event-pool", new SoftReferenceObjectPool(
477: new EventFactory()));
478: builder.addComponent("error-handler", new NoopSink());
479: builder.addComponent(SocketServer.class, new CustomComponent(
480: SocketServerImpl.class).redirectCall(3, "event-pool")
481: .redirectCall(4, "pipeline-pool").redirectCall(5,
482: "error-handler"));
483: }
484:
485: protected InetAddress createInetAddress()
486: throws UnknownHostException {
487: return InetAddress.getByName(m_address);
488: }
489:
490: protected HttpServer createJettyServer() throws MultiException {
491: final HttpServer jetty = new HttpServer();
492:
493: configureJetty(jetty);
494:
495: return jetty;
496: }
497:
498: protected void configureJetty(final HttpServer server)
499: throws MultiException {
500: final HttpContext context = new HttpContext();
501: context.setContextPath("/");
502: context.setResourceBase(m_directory);
503: context.addHandler(new ResourceHandler());
504: server.addContext(context);
505:
506: server.start();
507: }
508: }
|