HelloWorldWithSessionsApp.java

/***************************************************************************
   Copyright 2012 Emily Estes

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
***************************************************************************/
package net.metanotion.web.examples;


import java.util.concurrent.atomic.AtomicInteger;

import net.metanotion.util.Unknown;
import net.metanotion.web.Default;
import net.metanotion.web.RequestObject;
import net.metanotion.web.SessionFactory;
import net.metanotion.web.concrete.DefaultDispatcher;
import net.metanotion.web.concrete.HttpUtil;
import net.metanotion.web.servlets.ServerUtil;

/** This is a web app server that tracks sessions and visits. Each request is answered
with a sequential number identifying the session, the number of visits, and the resource
the visitor requested. */
public final class HelloWorldWithSessionsApp implements SessionFactory<RequestObject> {
	/** Our app server is just another Java program. So we start up in main like normal and
	initialize our app with whatever needs initializing. When we're ready to listen for requests
	we use the net.metanotion.web.servlets.ServerUtil class to start up Jetty + our framework
	servlet in embedded mode.
		@param args The command line arguments to our application... but this application doesn't use
			any command line arguments.
	*/
	public static void main(String[] args) {
		try {
			/** We're using the "DefaultDispatcher", this dispatcher assumes your session implements
			the net.metanotion.web.Default interface. We use this class as the SessionFactory. */
			ServerUtil.launchJettyServer(8080, new DefaultDispatcher(), new HelloWorldWithSessionsApp());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/** This integer is incremented for each HTTP request. */
	public final AtomicInteger visits = new AtomicInteger();
	/** This integer is incremented for each Session we create. */
	private final AtomicInteger session = new AtomicInteger();

	/** Produce an instance of {@link net.metanotion.util.Unknown} for each session.
	This method is called once for each session created. While this is using the default
	session mechanism, more advanced strategies could be implemented since the RequestObject
	is provided(e.g. encrypted cookie "flyweight sessions", accessing distributed backing stores
	or database backed sessions, etc.).
		@param ro The request object associated with the request generating a new session.
		@return A new instance of a session object that will be cached for future usage.
	*/
	@Override public Unknown newSession(RequestObject ro) {
		return new Session(this, session.getAndIncrement());
	}

	/** This class represents a user session and implements the Unknown interface.
	It also implements {!link net.metanotion.web.Default} interface since instances of this
	class ALSO handle messages dispatched to the Default interface. */
	private static final class Session implements Default, Unknown {
		/** The unique, sequential, number assigned to this session. */
		private final int sessionID;
		/** The application object, mostly so we can increment the visit counter per request. */
		private final HelloWorldWithSessionsApp app;
		public Session(HelloWorldWithSessionsApp app, int sessionID) {
			this.app = app;
			this.sessionID = sessionID;
		}

		/** Implement the Unknown interface. This implementation is not strictly speaking "correct"
		since it doesn't check the value of theInterface, however, since our app consists of only
		one interface, the only possible value is Default, and this class provides the implementation
		of Default, so return ourself. */
		@Override public Object lookupInterface(Class theInterface) { return this; }
		/** Answer an HTTP request by returning a VERY simple webpage containing the session number,
		visit counter, and resource the user requested. */
		@Override public Object get(String url) {
			return HttpUtil.newHtmlString("<html><body>Hi user " + sessionID +
				" and hit " + app.visits.getAndIncrement() +
				" on URI: " + url + "</body></html>");
		}
	}
}