Aspect-oriented programming with AspectJ

Aspect-Oriented Programming with AspectJ™

the AspectJ.org team Xerox PARC

Bill Griswold, Erik Hilsdale, Jim Hugunin, Vladimir Ivanovic, Mik Kersten, Gregor Kiczales, Jeffrey Palm

partially funded by DARPA under contract F30602-97-C0246

this tutorial is about...

• using AOP and AspectJ to: – improve the modularity of crosscutting concerns • design modularity • source code modularity • development process • aspects are two things: – concerns that crosscut [design level] – a programming construct [implementation level] • enables crosscutting concerns to be captured in modular units • AspectJ is: – is an aspect-oriented extension to Java™ that supports general-purpose aspect-oriented programming

2 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 1 Aspect-oriented programming with AspectJ

problems like… logging is not modularized

• where is logging in org.apache.tomcat – red shows lines of code that handle logging – not in just one place – not even in a small number of places 3 AspectJ Tutorial

problems like… session expiration is not modularized

ApplicationSession StandardSession

/* public void invalidate() { package org.apache.tomcat.session; /** * ======serverSession.removeApplicationSession (context); /** * Bind an object to this session, using the specified name.If an object * * Return the HttpSession for which this * of the same name is already bound to this session, the jectob is * The Apache Software License, Version 1.1 // remove everything in the session importjava. io.IOException; object *replaced. * Copyright (c) 1999 The Apache Software Foundation. All rights Enumeration enum = values.keys(); importjava. io.ObjectInputStreamObjectOutputStream; **/ is the facade. *

After this method executes, and if the object implements * reserved. while( enum.hasMoreElements()) { importjava. io.Serializable ; public HttpSessiongetSession (){ * HttpSessionBindingListener ,the container calls * Stringname = (String) enum.nextElement(); import java.util.Enumeration; * valueBound() on the object. * Redistribution and use in source and binary forms, with or thoutwi removeValue(name); import java.util. Hashtable; return (( HttpSession) this); * * modification, are permitted provided that the following conditions } import java.util.Vector; * @ param name Name to which the object is bound, cannot be null * are met: valid= false; importjavax.servlet .ServletException.http.HttpSession ; } * @ param value Object to be bound, cannot be null * 1. Redistributions of source code must retain the above copyright } importjavax.servlet .http.HttpSessionBindingEvent ; *@exception IllegalStateException if this method is called on an * notice, this list of conditions and the following disclaimer. importjavax.servlet .http.HttpSessionBindingListener; // ------* invalidated session * public booleanisNew () { importjavax.servlet .http.HttpSessionContext ; Session Public Methods * * 2. Redistributions in binary form must reproduce the above pyrightco if(! valid) { importorg.apache.tomcat. catalina.*; * @deprecated As of Version 2.2, this method is replaced by * notice,the documentation this list ofand/or conditions other materialsand the following provided disclaimwith theerin Stringmsg = sm.getString (" applicationSession.session.ise"); importorg.apache.tomcat.util. StringManager; /** **/ setAttribute() * distribution. thrownew IllegalStateException(msg); * Update the accessed time information for this session. publicvoid putValue(String name, Object value) { * } /** This method * 3. The end -user documentation included with the redistribution, if * Standard implementation of the Session * should be called by the context when a request comes in setAttribute (name, value); * any, must include the followingacknowlegement : if( thisAccessTime== creationTime) { interface. This object is fora particular * "This Apache product Software includes Foundation software (http://www.apache.org/)." developed by the } returnelse {true; persistent*serializable storage, so or that transferred it can be stored in **/ session, even if the application does not reference it. } * Alternately, thisacknowlegementmay appear in the software returnfalse; * to a different JVM for distributable session public void access() { itself, } support. /** * if and wherever such third -partyacknowlegementsnormallyappear. } *

this. lastAccessedTime = this. thisAccessedTime ; * Remove the object bound with the specified name from thissession. If * * IMPLEMENTATION NOTE: An instance of this this. thisAccessedTime = System. currentTimeMillis (); * the session does not have an object bound with this name,this method * 4. Foundation"The names "Themust Jakartanot be usedProject", to endorse "Tomcat", or promoteand "Apache product Softwares /** class* internal represents (Session) both the and application level } this. isNew=false; *

does nothing. derived *@deprecated (HttpSession) view of the session. * After this method executes, and if the object implements * from this software without prior written permission. Forritten w */ * However, because the class itself is not declared * HttpSessionBindingListener ,the container calls * permission, please contact [email protected]. public, Java logic outside /** * valueUnbound () on the object. * publicvoid putValue(String name, Object value) { * of the org.apache.tomcat.session * Perform the internal processing required to invalidate * * 5. norProducts may "Apache" derived appearfrom this in theirsoftware names may without not be calledprior writt "Apache"en } setAttribute (name, value); package*HttpSession cannot castview an of this instance back to a this session,* without triggering an exception if the session has * @ param name Name of the object to remove from this session. * permission of the Apache Group. Session view. already expired. *@exception IllegalStateException if this method is called on an * publicvoid setAttribute(String name, Object value) { * */ * invalidated session * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED if(! valid) { * @author Craig R. McClanahan public void expire() { */ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES Stringmsg = sm.getString (" applicationSession.session.ise"); * @version $Revision: 1.2 $ $Date: 2000/05/15 publicvoid removeAttribute(String name) { * DISCLAIMED.OF MERCHANTABILITY IN NO EVENTAND FITNESS SHALL FORTHE AAPACHE PARTICULAR SOFTWARE PURPOSE FOUNDATION ARE OR thrownew IllegalStateException(msg); 17:54:10*/ $ sessions // Remove this session from our manager's active synchronized (attributes) { * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, } if ((manager != null) && (manager instanceof Objectobject = attributes.get(name); * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUTNOT final classStandardSession ManagerBase)) if (object == null) * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSSOF if (name == null) { implementsHttpSession, Session { ((ManagerBase ) manager).remove(this); return; * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSEDAND Stringmsg = sm.getString (" applicationSession.value.iae"); attributes.remove(name); * ORON TORTANY THEORY(INCLUDING OF LIABILITY, NEGLIGENCE WHETHER OR OTHERWISE) IN CONTRACT, ARISING STRICT IN ANYLIABI LITY,YWAOUT thrownew IllegalArgumentException (msg); // ------//Vector Unbind results any objects= new associatedVector(); with this session //if (object System.out.instanceofprintln HttpSessionBindingListener("Removing attribute ){ " + name ); * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITYOF } ------Constructors Enumeration attrs =getAttributeNames(); ((HttpSessionBindingListener ) object).valueUnbound * SUCH DAMAGE. while( attrs .hasMoreElements()){ (new HttpSessionBindingEvent((HttpSession ) this, name)); * ======removeValue(name); // remove any existing binding Stringattr= (String) attrs.nextElement(); } * /** results. addElement(attr); } * individualsThis software on consistsbehalf ofof thevoluntary Apache Softwarecontributions Foundation. made by Formanymore if HttpSessionBindingEvent(value != null && value instanceofe= HttpSessionBindingListener){ specified* Construct Manager. a new Session associated with the }Enumeration names = results.elements(); } * information on the Apache Software Foundation, please see new HttpSessionBindingEvent(this,name); * while(names. hasMoreElements()){ * . * @ param manager The manager with which this Stringname = (String) names. nextElement(); * ((HttpSessionBindingListener)value).valueBound (e); Session is associated removeAttribute (name); /** * [Additional notices, if required by prior licensing conditions] } */ } * Remove the object bound with the specified name from thissession. If **/ values.put(name, value); public StandardSession(Manager manager) { // Mark this session as invalid *does thenothing. session does not have an object bound with this name,this method } super(); setValid(false); *

this.manager = manager; * After this method executes, and if the object implements /** } * HttpSessionBindingListener ,the container calls package org.apache.tomcat.session; *@deprecated } * valueUnbound () on the object. importorg.apache.tomcat.core.*; public*/ Object getValue(String name) { /** * @ param name Name of the object to remove from this session. importorg.apache.tomcat.util. StringManager; returngetAttribute (name); // ------* Release all object references, and initialize instance * importjava. io.*; } ------Instance Variables variables,in *@exception IllegalStateException if this method is called on an importjava.net.*; * preparation for reuse of this object. * invalidated session import java.util.*; publicObject getAttribute(String name) { */ * importjavax.servlet .*;.http.*; if(! Stringvalid) msg{ = sm.getString (" applicationSession.session.ise"); /*** The collection of user data attributes publicvoid recycle() { * @deprecated removeAttribute As of Version() 2.2, this method is replaced by associated with this Session. // Reset the instance variables associated with this */ /** thrownew IllegalStateException(msg); */ Session publicvoid removeValue (String name) { * Core implementation of an application level session } privateHashtableattributes= new Hashtable(); attributes.clear(); * creationTime =0L; removeAttribute(name); * @author JasonJames HunterDuncan [ [email protected]] [ [email protected]] if String(name == msgnull)= { sm.getString (" applicationSession.value.iae"); /** idlastAccessedTime = null; = 0L; } * @author James Todd [[email protected]] * The time this session was created, in manager= null; */ thrownew IllegalArgumentException (msg); milliseconds since midnight, maxInactiveInterval =-1; SessionInterceptor StandardManager StandardSessionManager } * January 1, 1970 GMT. isNew = true; /** publicclass ApplicationSession implementsHttpSession{ */ isValid = false; * Bind an object to this session, using the specified name.If an object privateStringManagersm = } return values.get(name); private longcreationTime = 0L; // Tell our Manager that this Session has been recycled * replaced.of the same name is already bound to this session, the jectob is package org.apache.tomcat.request; StringManager.getManager ("org.apache.tomcat.session"); if ((manager != null) && (manager instanceof *

public int beforeBody ( Request rrequest, Response response ) { privateHashtablevalues = new Hashtable (); /** /** ManagerBase)) * After this method executes, and if the object implements importorg.apache.tomcat.core.*; StringreqSessionId =response. getSessionId (); private String id; *@deprecated *The session identifier of this Session. ((ManagerBase ) manager).recycle(this); * HttpSessionBindingListener ,the container calls importorg.apache.tomcat.util.*; java. io.*; if(reqSessionId debug>0 ) ==null)cm.log("Before Body " +reqSessionId ); package org.apache.tomcat.session; package org.apache.tomcat.session; // ------Public privateServerSession serverSession ; */ */ * valueBound() on the object. importjava.net.*; return0; // ------Lifecycle Methods Methods private Context longcreationTime context;= System. currentTimeMillis ();; publicEnumeration String[] getValueNamese getAttributeNames= (){ (); private String id = null; } * @ param name Name to which the object is bound, cannot be null import java.util.*; importjava. io.IOException; private longthisAccessTime =creationTime ; Vector names = new Vector(); * @ param value Object to be bound, cannot be null importjavax.servlet .http.*; import java.util.Enumeration; importjava. io.IOException; private longlastAccessed =creationTime ; /** // ------Session * // GS, set the path attribute to the cookie. This way import java.util.Vector;java.util. Hashtable; /*** Configure this component, based on the specified configuration importjavax.servlet .http.Cookie; privateintinactiveInterval =-1; while(e. hasMoreElements ()) { * Descriptive information describing this Package Methods *@exception IllegalArgumentExceptionif an attempt is made to add a /*** Will process the request and determine the session Id, and tseit //context. multiple session cookies can be used, one for each importorg.apache.tomcat. catalina.*; * parameters. This method should be called immediately afterthe importjavax.servlet .http.HttpSession ; /** privateboolean valid = true; names.addElement(e.nextElement()); Session implementation. * non-serializable object in an environment marked distributable. * in the Request. StringsessionPath=rrequest .getContext(). getPath (); importjavax.servlet .http.Cookie; * component instance is created, and before start() importorg.apache.tomcat. catalina.*; * Mark the specified session's last accessed time. This ouldsh be ApplicationSession (Stringid, ServerSessionserverSession , } private*/ static final String info = /** * @exception invalidatedIllegalStateException session if this method is called on an * It also marks the session as accessed. if(sessionPath .length() == 0) { importjavax.servlet .http.HttpSession ; * is called. * called for each request byRequestInterceptor a . Context context) { String[]valueNames = new String[names.size()]; "StandardSession/1.0"; * Return the isValid flag for this session. */ * sessionPath="/"; importorg.apache.tomcat.util. StringManager; * import org.apache.tomcat.core.Context; this. serverSession=serverSession; */ publicvoid setAttribute(String name, Object value) { * This implementation only handles Cookies sessions, please extend or } import org.w3c.dom.Node;org.w3c.dom.NamedNodeMap; * @ (FIXME:param parameters What Configuration object type shouldparameters this for really this be?)component import org.apache.tomcat.core.Request; * this.context = context; names.copyInto (valueNames); boolean isValid() { * add new interceptors for other methods. // GS, piggyback thejvm route on the session id. * importorg.apache.tomcat.core.Response; * @ param session The session to be marked this.id = id; /** if ((manager != null) && manager. getDistributable()&& */ if(!sessionPath.equals("/")) { *@exception IllegalStateException if this component has already been importorg.apache.tomcat.core. SessionManager ; */ this. inactiveInterval = context.getSessionTimeOut(); returnvalueNames ; **/ The last accessed time for this Session. return (this.isValid); !(valuethrowinstanceofnew IllegalArgumentException Serializable )) publicclass SessionInterceptor extends BaseInterceptor implements RequestInterceptor{ StringjvmRoute =rrequest.getJvmRoute(); /** * configured and/or started public void accessed(Context ctx,Request req , String id) { } private longlastAccessedTime=creationTime; } (sm. getString ("standardSession .setAttribute.iae ")); if(null != jvmRoute){ * Standard implementation of the Manager interface thatprovides *@exception LifecycleException if this component detects a fatal error importorg.apache.tomcat.util. SessionUtil ; if(this. inactiveInterval!= -1) { // GS, separates the session id from the jvm route reqSessionId=reqSessionId + SESSIONID_ROUTE_SEP + jvmRoute; * anno optional,session persistence configurable, or distributable maximum number capabilities, of active sessions but doesallowed.support **/ in the configuration parameters it was given HttpSession session=findSession (ctx,id); this. inactiveInterval*= 60; publicEnumeration getAttributeNames(){ synchronized (attributes) { staticint debug=0; final char SESSIONID_ROUTE_SEP = '.'; }} *

publicvoid configure(Node parameters) if( session == null) return; } if(! valid) { /** /** removeAttribute (name); ContextManagercm; * Lifecycle configuration of this component assumes an XML node throwsLifecycleException{ /** if(session instanceofSession) } Stringmsg = sm.getString (" applicationSession.session.ise"); associated.* The Manager with which this Session is * Set the isNew flag for this session. attributes.put(name,if (valueinstanceof HttpSessionBindingListener value); ) Cookie cookie = new Cookie("JSESSIONID", * in the following format: ((Session) session).access(); ServerSession getServerSession () { thrownew IllegalStateException(msg); */ * @ paramisNew The new value for the isNew ((HttpSessionBindingListener ) value). valueBound public SessionInterceptor () { reqSessionId); * // Validate and update our current component state * Specialized implementation of org.apache.tomcat.core. SessionManager returnserverSession; } private Manager manager = null; flag (new HttpSessionBindingEvent((HttpSession ) this, name)); } cookie. setMaxAge(-1); * & lt ;ManagercheckIntervalclassName="org.apache.tomcat.session.="60" maxActiveSessions="-1"StandardManager" if(configured) thrownew LifecycleException * that adapts to the new component-based Manager implementation. } */ } publicvoid setDebug(int i ) { cookie. setPathsetVersion(sessionPath(1); ); * maxInactiveInterval=" -1" /> (sm. getString ("standardManager .alreadyConfigured")); *

// cache the HttpSession -avoid another find HashtablevaluesClone = Hashtable( )values.clone(); void setNew (booleanisNew ) { System.out.println("Set debug to " + i); * configured= true; * XXX -At present, use of StandardManager is hard coded, req.setSession ( session ); /*** Called by context when request comes in so that accessesand return (Enumeration)valuesClone .keys(); /*** The maximum time interval, in seconds, between this. isNew=isNew; } debug=i; response. addHeader(CookieTools .getCookieHeaderName(cookie), * where you can adjust the following parameters, with defaultalues v if(parameters == null) } *inactivities can be dealt with accordingly. } clientrequests before } CookieTools .getCookieHeaderValue(cookie)); * in square brackets: return; * and lifecycle configuration is not supported. */ * the servletcontainer may invalidate this } // ------HttpSession Private Methods cookie. setVersion (0); * ulli<>> checkInterval -The interval (in seconds) between background // Parse and process our configuration parameters *

session. A negative time publicthis.cm=cm;void setContextManager(ContextManager cm ) { response. addHeader(CookieTools .getCookieHeaderNamegetCookieHeaderValue(cookie),(cookie)); * thread checks for expired sessions. [60] if(!("Manager".equals(parameters. getNodeName ()))) * IMPLEMENTATION NOTE: Once we commit to the new // XXX should we throw exception or just return null ?? void accessed() { /** * indicates that the session should never time } * li<> maxActiveSessions -The maximum number of sessions allowed to return; Manager/Session public HttpSessionfindSession (Context ctx, String id ) { // fromset last the accessedprevious to accessthisAccessTime as it will be left over **/ @deprecated out.*/ /*** Set the isValid flag for this session. /*** Read a serialized version of this session object from thespecified return 0; * be active at once, or -1 for no limit. [-1] NamedNodeMap attributes = parameters.getAttributes (); * paradigm, I would suggest moving the logic implemented here ackb into try { lastAccessed =thisAccessTime ; privateintmaxInactiveInterval =- 1; * * object input stream. public int requestMap (Request request ) { } * li<> maxInactiveInterval -The default maximum number of seconds of Node node = null; thisAccessTime = System. currentTimeMillis (); publicvoid removeValue (String name) { * @ paramisValid The new value for the *

StringsessionId = null; * inactivitya session, before or- 1 which for no the limit.servlet Thiscontainer value should is allowed be overridden to time from out node = attributes.getNamedItem("checkInterval "); * the core level. The Tomcat.Next "Manager" interface acts morelike a Session session = manager.findSession (id); removeAttribute(name); isValid flag * IMPLEMENTATION NOTE: The reference to the owningManager Cookie cookies[]=request.getCookies(); // assert !=null /** Notification of context shutdown * the default session timeout specified in the web application deployment if (node != null) { * collection class, and has minimal knowledge of the detailedequest r if(session!=null) validate(); } /** */ * is not restored by this method, and must be set explicitly. */ * descriptor, if any. [ -1] try{ * processing semantics of handling sessions. return session.getSession (); } publicvoid removeAttribute(String name) { not.* Flag indicating whether this session is new or void setValid (booleanisValid ){ * @ param stream The input stream to read from for( inti=0; i setCheckInterval (Integer. parseInt (node.getNodeValue())); *

} catch ( IOExceptione) { void validate() { if(! valid) { */ this. isValid =isValid; * Cookiecookie = cookies[i]; throwsTomcatException * } catch Throwable( t) { // if we have an inactive interval, check to see if we've exceeded it Stringmsg = sm.getString (" applicationSession.session.ise"); privateboolean isNew = true; } *@exception ClassNotFoundExceptionif an unknown class is specified { * @version@author $Revision:Craig R. 1.1.1.1McClanahan $ $Date: 2000/05/02 21:28:30 $ } ; // XXX -Throw exception? for* XXX -At present, there is no way (via theSessionManager interface) } if( inactiveInterval!= -1){ *@exception IOException if an input/output error occurs if (cookie.sessionIdgetName=cookie. ().equals("JSESSIONID"))getValue(); { if(ctxctx.getSessionManager.getDebug() > 0 )(). ctxremoveSessions.log("Removing(ctx sessions); from " + ctx ); */ } return (null); intthisInterval = thrownew IllegalStateException(msg); */ sessionId =validateSessionId(request,sessionId); } * a Context to tell the Manager that we create what the defaultsession} (int )(System. currentTimeMillis () -lastAccessed ) / 1000; } /*** Flag indicating whether this session is valid HttpSession// ------Properties privatethrows voidClassNotFoundExceptionreadObject (ObjectInputStream,IOExceptionstream){ if( sessionId !=null){ public final classStandardManager node = attributes.getNamedItem("maxActiveSessions"); * timeout for this web application (specified in the deployment if thisInterval( >inactiveInterval) { if (name == null) { or not. request.setRequestedSessionIdFromCookie(true); extendsManagerBase if (node != null) { descriptor) invalidate(); Stringmsg = sm.getString (" applicationSession.value.iae"); */ // Deserialize the scalar instance variables (except Manager) } } implements Lifecycle, Runnable { try{ setMaxActiveSessions(Integer.parseInt (node.getNodeValue())); * should be. public HttpSessioncreateSession (Context ctx){ } privateboolean isValid = false; /** creationTime =((Long) stream. readObject()).longValue (); }} } catch Throwable( t) { * return manager.createSession ().getSession(); } thrownew IllegalArgumentException (msg); * Return the time when this session was created, in id = (String) stream. readObject (); // ------Instance Variables ; // XXX -Throw exception? * @author Craig R. McClanahan } } } /** milliseconds* midnight, since January 1, 1970 GMT. lastAccessedTimemaxInactiveInterval= ((Long)= ((Integer) stream. stream.readObjectreadObject()). longValue()).intValue(); (); Stringsig=";jsessionid="; } // HTTP SESSION IMPLEMENTATION METHODS Object o = values.get(name); * The string manager for this package. * isNew = ((Boolean) stream. readObject ()).booleanValue(); intfoundAt =-1; } */ */ *@exception IllegalStateException ifthis method is isValid = ((Boolean) stream.readObject ()).booleanValue(); if( debug>0 ) cm.log(" XXX RURI=" + request.getRequestURI ()); /*** The interval (in seconds) between checks for expired sessions. node = attributes.getNamedItem("maxInactiveInterval"); /** publicString getId(){ if (o instanceof HttpSessionBindingListener ) { privateStringManagersm = called on an if ((sessionIdfoundAt =request. getRequestURI().indexOfsubstring(sig())!=foundAt-1){+sig.length()); */ if (node != null) { public final classStandardSessionManager * Remove all sessions because our associated Context is beingshut if(valid) { HttpSessionBindingEvente= * invalidated session // Deserialize the attribute count and attribute values // rewrite URL, do I need to do anything more? privateint checkInterval = 60; try{ implementsSessionManager { down. } returnelse id;{ new HttpSessionBindingEvent(this,name); StringManager; .getManager("org.apache.tomcat.session") public*/ long getCreationTime() { intfor (n =int ((Integer)i= 0; i < stream.n; i++) { readObject ()).intValue (); request. setRequestURI(request.getRequestURI(). substring(0, foundAt)); setMaxInactiveInterval(Integer.parseInt(node. getNodeValue ())); * Stringmsg = sm.getString (" applicationSession.session.ise"); ((HttpSessionBindingListener)o). valueUnbound (e); Stringname = (String) stream. readObject(); sessionId=validateSessionId (request, sessionId ); } catch Throwable( t) { * @ paramctx The context that is being shut down } return (this.creationTime); Object value = (Object) stream.readObject (); if sessionId( !=null){ /***Has this component been configured yet? } ; // XXX -Throw exception? */ thrownew IllegalStateException(msg); /** attributes.put(name, value); } request.setRequestedSessionIdFromURL(true); */ } // ------} values.remove(name); * The HTTP session context associated with this } } } privateboolean configured= false; Constructors publicvoid removeSessions(Contextctx) { } } session.*/ } return 0; } publiclong getCreationTime() { publicvoid setMaxInactiveInterval(intinterval) { privatestatic HttpSessionContext sessionContext /** } // XXX XXX a manager may be shared by multiple if(valid) { if(! valid) { =null; * Return the session context with which this session is /*** The descriptive information about this implementation. // contexts, we just want to remove the sessions ctxof ! returncreationTime; Stringmsg = sm.getString (" applicationSession.session.ise"); associated. /** // XXXWe may what still is the set correct it and behaviorjust return if thesession session invalid. is invalid? */ /** } else { * * Write a serialized version of this session object to thespecified private static final String info = StandardManager" /1.0"; /** *Create a new SessionManager that adapts to the corresponding // The manager will still run after that ( i.e. keep database Stringmsg = sm.getString (" applicationSession.session.ise"); }thrownew IllegalStateException(msg); /*** The current accessed time for this session. and has *no @deprecated As of Version 2.1, this method is deprecated *

object output stream. /** Validate and fix the session id. If the session is not alidv return null. * Prepare for the beginning of active use of the public methods of this Manager // connection open thrownew IllegalStateException(msg); */ * replacement. It will be removed in a future version of * IMPLEMENTATION NOTE: The owning Manager will notbestored * It will also clean up the session from load -balancingstrings. * component. This method should be called after configure(), *implementation. if(manager instanceofLifecycle){ } inactiveInterval = interval; private longthisAccessedTime=creationTime; the * in the serialized representation of this Session. Aftercalling *@return sessionId, or null if not valid /*** The maximum number of active Sessions allowed, or- 1for no limit. * and before any of the public methods of the component areutilized. */ try{ } } * Java Servlet API. * readObject(), you must set the associated Manager private*/ String validateSessionId(Requestrequest, String sessionId ){ */ *@exception IllegalStateException if this component has not yet been */ *explicitly. // GS, We piggyback the JVM id on top of the session cookie protected intmaxActiveSessions =- 1; * configured (if required for this component) public StandardSessionManager(){ ((Lifecycle) manager).stop(); /*** publicif(! intvalid) getMaxInactiveInterval{ (){ public HttpSessionContextgetSessionContext (){ *

IMPLEMENTATION NOTE: Any attribute that is not Serializable // Separate them ... *@exception IllegalStateException if this component has already been } catch LifecycleException( e){ *@deprecated Stringmsg = sm.getString (" applicationSession.session.ise"); // ------if( sessionContext==null) * will be silently ignored. If you do not want any such tributes,at * started manager = newStandardManager (); throw new IllegalStateException("" + e); */ ------Session Properties sessionContext= new StandardSessionContext(); * be sure the distributable property of our sociatedas if( debug>0 ) cm.log("Orig sessionId " +sessionId ); /*** The string manager for this package. *@exception that preventsLifecycleException this componentif from this being component used detects a fatal error if(manager instanceofLifecycle){ } thrownew IllegalStateException(msg); return sessionContext( ); * Manager is set to true. if int(null idex != sessionId=sessionId) { .lastIndexOf (SESSIONID_ROUTE_SEP); */ */ public HttpSessionContextgetSessionContext (){ } * if( idex > 0) { privateStringManagersm = public void start() throwsLifecycleException { try{ } } return newSessionContextImpl (); returninactiveInterval; /*** Set the creation time for this session. This } * @ param stream The output stream to write to sessionId =sessionId .substring(0,idex); StringManager.getManager ("org.apache.tomcat.session"); ((Lifecycle) manager).configure(null); } method is called by the *@exception IOException if an input/output error occurs } // Validate and update our current component state ((Lifecycle) manager).start(); } publiclong getLastAccessedTime(){ } * Manager when an existing Session instance is // ------*/ } /** if(!configured) thrownew LifecycleException } catch LifecycleException( e){ if(valid) { reused. HttpSessionPublicMethods private voidwriteObject(ObjectOutputStreamstream) throwsIOException{ if( sessionId!= null && sessionId.length()!=0) { * Has this component been started yet? (sm. getString ("standardManager .notConfigured")); returnlastAccessed; * // GS, We are in a problem here, we may actually get */ if(started) throw new IllegalStateException("" + e); /** } Stringelse {msg = sm.getString (" applicationSession.session.ise"); //------**/ @ param time The new creation time /** //stream. WritewriteObject the scalar(new instance Long(creationTime variables (except)); Manager) // multiple Session cookies (one for the root privateboolean started = false; thrownew LifecycleException } * Used by context to configure the session manager's inactivity publicvoid setCreationTime(long time) { * Return the object bound with the specified name in this stream. writeObject(id); // context and one for the real context... or old ssionse (sm. getString ("standardManager .alreadyStarted ")); } timeout. thrownew IllegalStateException(msg); session, or stream. writeObject(new Long(lastAccessedTime)); // cookie. We must check for validity in the currentcontext. /** started= true; * } this. creationTime = time; * null if no object is bound with that name. stream. writeObject(newInteger( maxInactiveInterval )); ContextSessionManagerctx=request. sM =getContextctx.getSessionManager(); (); * The background thread. // Start the background reaper thread * The SessionManager may have some default session time out, the } this. lastAccessedTime = time; * stream. writeObject(newBoolean( isNew )); if(null != sM .findSession (ctx ,sessionId)){ */ threadStart(); } * Context on the other hand has it's timeout set by the deployment this. thisAccessedTime = time; * @ param name Name of the attribute to be returned stream. writeObject(newBoolean( isValid )); sM.accessed(ctx, request, sessionId ); private Thread thread = null; } *@exception IllegalStateException ifthis method is // Accumulate the names of serializableattributes request.setRequestedSessionId(sessionId); } *descriptor (web. xml). This method lets the Contextconforgure the called on an Vector results = new Vector(); if( debug>0 ) cm.log(" Final session id " sessionId+ ); /** // ------Instance * session manager according to this value. * invalidated session Enumeration attrs =getAttributeNames(); } return sessionId ; * The background thread completion semaphore. /** Variables * /** */ while( attrs .hasMoreElements()){ } */ * Gracefully terminate the active use of the public methodsof this * @ param minutes The session inactivity timeout in minutes. session.* Return the session identifier for this publicObject getAttribute(String name) { StringObject attrvalue= (String)= attributes.get(attrs.nextElementattr ); (); return null; privateboolean threadDone= false; * component. This method should be the last one called ona given */ return (attributes.get(name)); if (valueinstanceofSerializable ) } *instance of this component. */ publicString getId(){ results.addElement (attr ); /** *@exception IllegalStateException if this component has not been started /** publicvoid setSessionTimeOut(int minutes) { } } * Name to register for the background thread. *@exception IllegalStateException if this component has already * The Manager implementation we are actually using. if(-1 != minutes) { return (this.id); */ * been stopped */ // The manager works with seconds... } /** //stream. SerializewriteObject the (newattribute Integer(results.size())); count and the attribute values privateString threadName = StandardManager" "; *@exception LifecycleException if this component detects a fatal error * Return an Enumeration of Enumeration names = results.elements(); * that needs to be reported private Manager manager = null; manager. setMaxInactiveInterval(minutes * 60); String objects while(names. hasMoreElements()){ // ------Properties public*/ void stop() throws LifecycleException { } /** * containing the names of the objects bound to this Stringname = (String) names. nextElement(); } * Set the session identifier for this session. session. stream.writeObject(name); // Validate and update our current component state } * @ param id The new session identifier *@exception IllegalStateException ifthis method is }stream.writeObject(attributes.get(name)); /** if(!started) */ called on an * Return the check interval (in seconds) for this Manager. thrownew LifecycleException publicvoid setId(String id) { * invalidated session public*/ int getCheckInterval() { started =(sm. false;getString ("standardManager .notStarted")); */ } if ((this.id != null) && (manager != null) && publicEnumeration getAttributeNames(){ return (this.checkInterval ); // Stop the background reaper thread ServerSession (manager((ManagerBaseinstanceof) ManagerBasemanager).remove(this);)) return (attributes.keys()); crosscut invalidate(StandardSession s): s & ( intlong getCreationTimegetMaxInactiveInterval()| ()| threadStop(); Object getAttribute (String) | } this.id = id; } Enumeration getAttributeNames()| //Session Expire sessions[]all active sessions=findSessions (); String[]getValueNames()| /** for ( inti = 0; i < sessions.length; i++) { if ((manager != null) && (manager instanceof voidinvalidate ()| * Set the check interval (in seconds) for this Manager. StandardSession session = ( StandardSession) sessions[i]; ManagerBase))((ManagerBase ) manager).add(this); /*** Return the object bound with the specified name in this booleanvoid removeAttributeisNew () |(String) | * if (!session. isValid ()) session, or void setAttribute(String, Object)); * @ paramcheckInterval The new check interval continue; package org.apache.tomcat.session; void validate() { } * null if no object is bound with that name. public*/ void setCheckInterval (int checkInterval){ }session.expire(); // if we have an inactive interval, check to see if * staticadvice( StandardSessions):invalidate(s) { importorg.apache.tomcat.core.*; // we've exceeded it * @ param name Name of the value to be returned before { ServerSessionManager this. checkInterval=checkInterval; } importorg.apache.tomcat.util. StringManager; /*** Return descriptive information about this *@exception IllegalStateException ifthis method is if (!s.throwisValid new ())IllegalStateException importjava. java.net.*; io.*; if( inactiveIntervalintthisInterval !== -1){ Session implementation and called on an (s.sm.getString(" standardSession." } import java.util.*; (int )(System. currentTimeMillis () -lastAccessed ) / 1000; * the corresponding version number, in the * invalidated session +thisJoinPoint .methodName // ------Private Methods importjavax.servlet .*; format * + ".ise")); /** importjavax.servlet .http.*; if thisInterval( >inactiveInterval) { * * @deprecated As of Version 2.2, this method is replaced } * Return descriptive information about this Manager implementationand /** invalidate(); <*/ ;description& gt;/& lt ;version>. by * getAttribute() } // XXX * the corresponding version number, in the format * Invalidate all sessions that have expired. /*** Core implementation of a server session ServerSessionManager ssm= publicString getInfo (){ */ package org.apache.tomcat.session; //sync'd for safty--no other thread should be getting something *& lt;description& gt;/& lt;version>. */ * ServerSessionManager.getManager (); publicObject getValue(String name) { // from this while we are reaping. This isn't the most optimal public*/ String getInfo (){ private voidprocessExpires() { * @author James Duncan Davidson [ [email protected]] return (this.info); importorg.apache.tomcat.util.*; // solution for this, but we'll determine something else later. long timeNow =System. currentTimeMillis(); * @author James Todd [[email protected]] ssm.removeSession(this); return getAttribute( (name)); } importorg.apache.tomcat.core.*; java. io.*; synchronized void reap() { return (this.info); Session sessions[] =findSessions (); */ } } } importjava.net.*; Enumeration enum = sessions.keys(); publicclass ServerSession{ } } // ------PrivateClass import java.util.*; } for ( inti = 0; i < sessions.length; i++) { /** importjavax.servlet .http.*; while( enum.hasMoreElements()) { StandardSessionif (!session. isValidsession()) = ( StandardSession) sessions[i]; privateStringManagersm = synchronized void invalidate() { * Return the last time the client sent a request /** Objectkey = enum .nextElement (); /** continue; StringManager.getManager ("org.apache.tomcat.session"); Enumeration enum =appSessions.keys(); associated with this * Return the set of names of objects bound to this /** /*** ServerSession session = ( ServerSession)sessions.get(key); * Return the maximum number of active Sessions allowed, or- 1 for intmaxInactiveInterval = session. getMaxInactiveInterval (); privateHashtablevalues = new Hashtable (); midnight,* session,January 1, as 1970 the number of milliseconds since session.*are no Ifsuch thereobjects, a zero -length array is returned. * Thisinterface, class isto aconform dummy implementationto the requirement of the that suchHttpSessionContext an objectbe returned * @author James Duncan Davidson [ [email protected]] session.reap(); * no limit. if maxInactiveInterval( < 0) privateHashtable String id; appSessions= new Hashtable (); whileObject( enumkey .hasMoreElements= enum .nextElement()) {(); * GMT. Actions that your application takes, * * when HttpSession .getSessionContext() is called. * @author Jason Hunter [ [email protected]] session.validate(); */ continue; private longcreationTime = System. currentTimeMillis ();; ApplicationSession appSession = such as getting or setting *@exception IllegalStateException ifthis method is * * @author James Todd [[email protected]] } public int getMaxActiveSessions(){ inttimeIdle (int ) timeNow(( = // Truncate,-session. do getLastAccessedTimenot round up ()) / 1000L); private longthisAccessTime =creationTime ; (ApplicationSession)appSessions.get(key); * a value associated with the session, do not called on an * @author Craig R. McClanahan */ } return (this.maxActiveSessions); if timeIdle( >= maxInactiveInterval) private longlastAccessed =creationTime ; affect the access time. * invalidated session * publicclass ServerSessionManagerimplements SessionManager{ synchronized voidremoveSession(ServerSession session) { session.expire(); privateintinactiveInterval =-1; appSession .invalidate(); public*/ long getLastAccessedTime(){ * @deprecated As of Version 2.2, this method is replaced * @deprecatedinterface Aswill of Javabe removedServlet in aAPI future 2.1 with version no replacement. of this API. The String id = session.getId(); } } ServerSession (String id) { } } by */ privateStringManagersm = } this.id = id; return (this.lastAccessedTime ); * getAttributeNames() StringManager.getManager ("org.apache.tomcat.session"); session.invalidate(); /** } publicvoid putValue(String name, Object value) { */ final classStandardSessionContext implementsHttpSessionContext{ privatestatic ServerSessionManager manager; // = newServerSessionManager(); sessions.remove(id); * Set the maximum number of actives Sessions allowed, or- 1 for /** if (name == null) { } public String[] getValueNames(){ protected int inactiveInterval= -1; } * no limit. * Sleep for the duration specified by the checkInterval publicString getId(){ Stringmsg = sm.getString (" serverSession.value.iae"); Vector results = new Vector(); private Vector dummy = new Vector(); publicvoid removeSessions(Context context) { * *property. } return id; thrownew IllegalArgumentException (msg); /** Enumeration attrs =getAttributeNames(); static { Enumeration enum = sessions.keys(); * @ param max The new maximum number of sessions */ } * Return the Manager within which this Session while( attrs .hasMoreElements()){ /** manager = newServerSessionManager(); public*/ void setMaxActiveSessions(intmax) { private voidthreadSleep(){ publiclong getCreationTime() { is valid. Stringattr= (String) attrs.nextElement(); * Return the session identifiers of all sessions defined } while( enum.hasMoreElements()) { try { returncreationTime ; removeValue(name); // remove any existing binding */ results. addElement(attr); *within this context. publicstatic ServerSessionManager getManager (){ ObjectServerSessionkey = enumsession.nextElement = ( ServerSession(); )sessions.get(key); this. maxActiveSessions=max; Thread.sleep( checkInterval* 1000L); } values.put(name, value); publicManager getManager () { }String names[] = new String[results.size()]; * @deprecated As of Java Servlet API 2.1 with no replacement. return manager; ApplicationSession appSession = } catch ( InterruptedException e) { publiclong getLastAccessedTime(){ } return (this.manager); for ( inti= 0; i < names.length; i++) * This method must return an empty Enumeration } session.getApplicationSession(context,false); } ; returnlastAccessed ; publicObject getValue(String name) { names[i] = (String) results.elementAt (i); * and will be removed in a future version of the API. } } if (name == null) { } return (names); */ privateHashtablesessions= new Hashtable (); if appSession( !=null) { // ------PublicMethods } Stringmsg = sm.getString (" serverSession.value.iae"); publicEnumeration getIds () { privateReaper reaper; } appSession.invalidate(); public ApplicationSessiongetApplicationSession (Contextcontext, /** } return (dummy.elements()); privateServerSessionManager (){ } booleanApplicationSessioncreate){ appSession = }thrownew IllegalArgumentException (msg); * Set the Manager within which this Session is reaper = Reaper.getReaper(); } /** /** (ApplicationSession)appSessions.get(context); valid. /** } reaper. setServerSessionManager(this); * Constructsettings specified and return by a thisnew sessionManager's object, properties. based onThe the sesefaultsion d * sessionStart thetimeouts. background thread that will periodically checkfor return values.get(name); * * Invalidates this session and unbinds any objects bound reaper.start(); /** * id will be assigned by this method, and available via thegetId () */ if( appSession == null && create) { } * @ param manager The new Manager to it. } * Used by context to configure the session manager's inactivitytimeout. * method of the returned session. If a new session cannotbe created private voidthreadStart(){ public*/ void setManager(Manager manager) { *@exception IllegalStateException ifthis method is /*** Return the HttpSessionassociated with the publicvoid accessed( Context ctx , Request req, String id ) { * The SessionManager may have some default session time out, the * for any reason, return null. // syncXXX to ensure valid? publicreturnEnumeration values.keys();getValueNames() { calledon * specified session identifier. ApplicationSession apS=( ApplicationSession)findSession(ctx,id); * Context on the other hand has it's timeout set by the deployment * if (thread != null) } this.manager = manager; * an invalidated session * if(apS ==null)return; *descriptor (web. xml). This method lets the Contextconforgure the *@exception instantiatedIllegalStateException for any reason if a new session cannot be return; appSession = newApplicationSession(id, this, context); */ * @ param id Session identifier for which to look up a session * session manager according to this value. */ threadDone= false; appSessions.put(context, appSession); publicvoid removeValue (String name) { } public void invalidate() { * ServerSessionservS .accessed(); servS =apS.getServerSession(); * @ param minutes The session inactivity timeout in minutes. publicSession createSession (){ thread = new Thread(this,threadName ); } values.remove(name); // Cause this session to expire * @deprecated This method As mustof Javareturn Servletnull andAPI will 2.1 be with removed no inreplacement. a apS.accessed(); */ thread. setDaemon(true); // XXX } /** expire(); * future version of the API. publicvoid setSessionTimeOut(int minutes) { if (( maxActiveSessions>= 0) && thread.start(); // make sure that we haven't gone over the end of our publicvoid setMaxInactiveInterval(intinterval) { * Return the maximum time interval, in seconds, */ // cache it -no need to compute it again if(-1 != minutes) { (sessions.size()thrownew IllegalStateException >= maxActiveSessions)) } // inactive interval -- if so, invalidate and create inactiveInterval = interval; between client requests } public HttpSessiongetSession (String id) { req.setSession (apS ); // The manager works with seconds... (sm. getString ("standardManager .createSession.ise")); // a newappSession } *before the servlet container will invalidate } }inactiveInterval= (minutes * 60); the session.* time A indicatesnegative that the session should never /** return (null); public HttpSessioncreateSession (Context ctx){ } return (super. createSession()); /** } returnappSession ; publicreturnint inactiveIntervalgetMaxInactiveInterval; (){ time out. * Return true if the client does not yet know } StringsessionId =SessionIdGenerator.generateId (); } * Stop the background thread that is periodically checkingfor } * aboutthe ServerSessionsession = newServerSession (sessionId); } **/ session timeouts. void removeApplicationSession(Context context) { *@exception IllegalStateException ifthis * session, or if the client chooses not to join the } sessions.put(sessionId, session); private voidthreadStop (){ appSessions.remove(context); //XXX method is called on session. For if(-1 !=inactiveInterval){ } //sync'd for safty--no other thread should be getting something **/ an invalidated session and the* example,client if the server used only cookie -based sessions, session.setMaxInactiveInterval(inactiveInterval); if (thread == null) /** // fromsolution this for while this, we arebut reaping.we'll determine This isn't something the most else optim lataler. public int getMaxInactiveInterval (){ * has disabled the use of cookies, then a session would be } return; * Called by context when request comes in so that accessesand new on each return session.getApplicationSession (ctx , true ); threadDone= true; *inactivities can be dealt with accordingly. synchronized void reap() { return (this.maxInactiveInterval); *request. } thread.interrupt(); */ Enumeration enum =appSessions.keys(); * public HttpSessionfindSession (Contextctx ,String id) { try { } called on*@exception an IllegalStateException ifthis method is ServerSession sSession=( ServerSession)sessions.get(id); thread.join(); void //accessed() set last accessed{ to thisAccessTime as it will be left over whileObject( enumkey .hasMoreElements= enum .nextElement()) {(); * invalidated session if(sSession==null) return null; } catch ( InterruptedException e) { // from the previous access ApplicationSession appSession = /** */ }; (ApplicationSession)appSessions.get(key); * Set the maximum time interval, in seconds, public booleanisNew () { returnsSession.getApplicationSession(ctx , false); lastAccessed =thisAccessTime ; between client requests } thread = null; thisAccessTime = System. currentTimeMillis (); appSession .validate(); the session.*before Athe negativeservlet container will invalidate return (this.isNew); } } } * time indicates that the session should never } } } time out. void validate() * // ------Background Thread * @ param interval The new maximum interval public*/ void setMaxInactiveInterval(intinterval) { /** **/ The background thread that checks for session timeouts danshutdown. this. maxInactiveInterval =interval; public void run() { } // Loop until the termination semaphore is set while(! threadDone){ threadSleepprocessExpires();(); } 4 } AspectJ Tutorial }

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 2 Aspect-oriented programming with AspectJ

problems like… session tracking is not modularized

HTTPRequest SessionInterceptor getCookies () getRequestURI()(doc) requestMap(request) getSession() beforeBody(req, resp) getRequestedSessionId() ......

Session HTTPResponse getAttribute(name) getRequest() setAttribute(name, val) setContentType(contentType) invalidate() getOutptutStream() ... setSessionId(id) ...

Servlet 5 AspectJ Tutorial

the cost of tangled code

• redundant code – same fragment of code in many places • difficult to reason about – non-explicit structure – the big picture of the tangling isn’t clear • difficult to change – have to find all the code involved – and be sure to change it consistently – and be sure not to break it by accident

6 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 3 Aspect-oriented programming with AspectJ

crosscutting concerns

HTTPRequest SessionInterceptor getCookies () getRequestURI()(doc) requestMap(request) getSession() beforeBody(req, resp) getRequestedSessionId() ......

Session HTTPResponse getAttribute(name) getRequest() setAttribute(name, val) setContentType(contentType) invalidate() getOutptutStream() ... setSessionId(id) ...

Servlet 7 AspectJ Tutorial

the AOP idea aspect-oriented programming

• crosscutting is inherent in complex systems • crosscutting concerns – have a clear purpose – have a natural structure • defined set of methods, module boundary crossings, points of resource utilization, lines of dataflow… • so, let’s capture the structure of crosscutting concerns explicitly... – in a modular way – with linguistic and tool support • aspects are – well-modularized crosscutting concerns

8 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 4 Aspect-oriented programming with AspectJ

language support to…

ApplicationSession StandardSession privateprivatelong intlastAccessedinactiveInterval =creationTime=-1; ; if inactiveInterval( intthisInterval != -1){ = stream.writeObject(newLong( lastAccessedTime )); (int )(System. currentTimeMillis () - void accessed() { lastAccessed)/ 1000; // set last accessed to thisAccessTime as it will be left over sM .accessed( ctx,request, sessionId ); // from the previous access if thisInterval( >inactiveInterval) { /* public void invalidate() { package org.apache.tomcat.session; /** lastAccessedthisAccessTime=thisAccessTime= System. currentTimeMillis; (); invalidate(); public void accessed( Contextctx ,Request req, String id ) { * ======serverSession.removeApplicationSession (context); /** * Bind an object to this session, using the specified name.If an object ServerSessionManager ssm= ApplicationSession apS=( ApplicationSession)findSession(ctx, * * Return the HttpSession for which this * of the same name is already bound to this session, the jectob is validate(); ServerSessionManager.getManager (); id); * The Apache Software License, Version 1.1 // remove everything in the session importjava. io.IOException; object *replaced. } if(apS ==null)return; * Copyright (c) 1999 The Apache Software Foundation. All rights Enumeration enum = values.keys(); importjava. io.ObjectInputStreamObjectOutputStream; **/ is the facade. *

After this method executes, and if the object implements ssm.removeSession(this); ServerSession servS =apS.getServerSession(); * reserved. while( enum.hasMoreElements()) { importjava. io.Serializable ; public HttpSessiongetSession (){ * HttpSessionBindingListener ,the container calls void //validate() if we have { an inactive interval, check to see if we've exceeded }} servS .accessed(); * Stringname = (String) enum.nextElement(); import java.util.Enumeration; * valueBound() on the object. it } apS.accessed(); * Redistribution and use in source and binary forms, with or thoutwi removeValue(name); import java.util. Hashtable; return (( HttpSession) this); * if( inactiveInterval!= -1){ * modification, are permitted provided that the following conditions } import java.util.Vector; * @ param name Name to which the object is bound, cannot be null intthisInterval = privatelong lastAccessedTime=creationTime; //req. cachesetSession it -no(apS need); to compute it again * are met: valid= false; importjavax.servlet .ServletException.http.HttpSession ; } * @ param value Object to be bound, cannot be null (int )(System. currentTimeMillis () -lastAccessed ) / 1000; } * 1. Redistributions of source code must retain the above copyright } importjavax.servlet .http.HttpSessionBindingEvent ; *@exception IllegalStateException if this method is called on an if thisInterval( >inactiveInterval) { * notice, this list of conditions and the following disclaimer. importjavax.servlet .http.HttpSessionBindingListener; // ------* invalidated session invalidate(); * public booleanisNew () { importjavax.servlet .http.HttpSessionContext ; Session Public Methods * } /** /** * 2. Redistributions in binary form must reproduce the above pyrightco if(! valid) { importorg.apache.tomcat. catalina.*; * @deprecated As of Version 2.2, this method is replaced by } associated* Return with this the last time the client sent a request * Invalidate all sessions that have expired. * notice,the documentation this list ofand/or conditions other materialsand the following provided disclaimwith theerin Stringmsg = sm.getString (" applicationSession.session.ise"); importorg.apache.tomcat.util. StringManager; /** **/ setAttribute() } * session, as the number of milliseconds since midnight, */ * distribution. thrownew IllegalStateException(msg); /*** Update the accessed time information for this session. publicvoid putValue(String name, Object value) { public long getLastAccessedTime (){ January 1, 1970 private voidprocessExpires() { * } /** This method* Perform the internal processing required to invalidate if(valid) { * GMT. Actions that your application takes, such as long timeNow =System. currentTimeMillis(); * 3. The end -user documentation included with the redistribution, if * Standard implementation of the Session this session,* should be called by the context when a request comes in setAttribute (name, value); returnlastAccessed; getting or setting Session sessions[] =findSessions (); * any, must include the followingacknowlegement : if( thisAccessTime== creationTime) { interface. This object is fora particular * without triggering an exception if the session has } else { accesstime. * a value associated with the session, do not affect the * "This Apache product Software includes Foundation software (http://www.apache.org/)." developed by the } returnelse {true; persistent*serializable storage, so or that transferred it can be stored in already**/ session,expired. even if the application does not reference it. } Stringmsg = sm.getString (" applicationSession.session.ise"); */ for ( inti = 0; i < sessions.length; i++) { * Alternately, thisacknowlegementmay appear in the software returnfalse; * to a different JVM for distributable session public void access()expire() { thrownew IllegalStateException(msg); publiclong getLastAccessedTime(){ StandardSessionif (!session. isValidsession()) = ( StandardSession) sessions[i]; itself, } support. /** } continue; * if and wherever such third -partyacknowlegementsnormallyappear. } *

this.// RemovelastAccessedTime this session= fromthis. ourthisAccessedTime manager's active; * Remove the object bound with the specified name from thissession. If } return (this.lastAccessedTime ); intmaxInactiveInterval = session. getMaxInactiveInterval (); * * IMPLEMENTATION NOTE: An instance of this sessions this. thisAccessedTime = System. currentTimeMillis (); * the session does not have an object bound with this name,this method } if maxInactiveInterval( < 0) * 4. Foundation"The names "Themust Jakartanot be usedProject", to endorse "Tomcat", or promoteand "Apache product Softwares /** class* internal represents (Session) both the and application level ManagerBase} this.if ((manager)) isNew=false; != null) && (manager instanceof *

does nothing. continue; derived *@deprecated (HttpSession) view of the session. ((ManagerBase ) manager).remove(this); * After this method executes, and if the object implements public longreturngetLastAccessedTimelastAccessed ; (){ inttimeIdle (int ) timeNow(( = // Truncate,-session. do getLastAccessedTimenot round up ()) / * from this software without prior written permission. Forritten w */ * However, because the class itself is not declared * HttpSessionBindingListener ,the container calls } this. lastAccessedTime = time; 1000L); * permission, please contact [email protected]. public, Java logic outside /** // Unbind any objects associated with this session * valueUnbound () on the object. if timeIdle( >= maxInactiveInterval) * publicvoid putValue(String name, Object value) { * of the org.apache.tomcat.session * PerformVector resultsthe internal = new processing Vector(); required to invalidate * session.expire(); * 5. norProducts may "Apache" derived appearfrom this in theirsoftware names may without not be calledprior writt "Apache"en } setAttribute (name, value); package*HttpSession cannot castview an of this instance back to a this session,* withoutEnumerationwhile( triggeringattrs attrs.hasMoreElements =angetAttributeNames exception()) if{ the ();session has * @ param name Name of the object to remove from this session. privatelong lastAccessed=creationTime ; /** } * permission of the Apache Group. Session view. already expired.Stringattr= (String) attrs.nextElement(); *@exception IllegalStateException if this method is called on an This method* Update the accessed time information for this session. } * publicvoid setAttribute(String name, Object value) { * */ results. addElement(attr); * invalidated session void accessed() { * should be called by the context when a request comes in * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED if(! valid) { * @author Craig R. McClanahan public} void expire() { */ // set last accessed to thisAccessTime as it will be left over fora particular * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES Stringmsg = sm.getString (" applicationSession.session.ise"); * @version $Revision: 1.2 $ $Date: 2000/05/15 Enumeration names = results.elements(); publicvoid removeAttribute(String name) { // from the previous access * session, even if the application does not reference it. * DISCLAIMED.OF MERCHANTABILITY IN NO EVENTAND FITNESS SHALL FORTHE AAPACHE PARTICULAR SOFTWARE PURPOSE FOUNDATION ARE OR thrownew IllegalStateException(msg); 17:54:10*/ $ sessions //while StringRemove(names. name thishasMoreElements= session(String) fromnames. ()) our{ manager'snextElement active(); synchronized (attributes) { lastAccessed =thisAccessTime ; */ /** * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, } if ((managerremoveAttribute != null)(name); && (manager instanceof Objectobject = attributes.get(name); thisAccessTime = System. currentTimeMillis (); public void access() { * Mark the specified session's last accessed time. This ouldsh be * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUTNOT final classStandardSession ManagerBase})) if (object == null) this. lastAccessedTime = this. thisAccessedTime ; * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSSOF if (name == null) { implementsHttpSession, Session { ((ManagerBase ) manager).remove(this); return; } this. thisAccessedTime = System. currentTimeMillis (); * called for each request byRequestInterceptor a . * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSEDAND Stringmsg = sm.getString (" applicationSession.value.iae"); // Mark this session as invalid attributes.remove(name); this. isNew=false; * * ORON TORTANY THEORY(INCLUDING OF LIABILITY, NEGLIGENCE WHETHER OR OTHERWISE) IN CONTRACT, ARISING STRICT IN ANYLIABI LITY,YWAOUT thrownew IllegalArgumentException (msg); // ------//VectorsetValid Unbind (false);results any objects= new associatedVector(); with this session //if (object System.out.instanceofprintln HttpSessionBindingListener("Removing attribute ){ " + name ); } * @ param session The session to be marked * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITYOF } ------Constructors } Enumeration attrs =getAttributeNames(); ((HttpSessionBindingListener ) object).valueUnbound void validate() { */ * SUCH DAMAGE. while( attrs .hasMoreElements()){ (new HttpSessionBindingEvent((HttpSession ) this, name)); // if we have an inactive interval, check to see if lastAccessedTime = 0L; * ======removeValue(name); // remove any existing binding Stringattr= (String) attrs.nextElement(); } // we've exceeded it public void accessed(Context ctx,Request req , String id) { * /** /** results. addElement(attr); } lastAccessedTime = ((Long) stream.readObject()). longValue (); HttpSession session=findSession (ctx,id); * individualsThis software on consistsbehalf ofof thevoluntary Apache Softwarecontributions Foundation. made by Formanymore if HttpSessionBindingEvent(value != null && value instanceofe= HttpSessionBindingListener){ specified* Construct Manager. a new Session associated with the variables,* Release}Enumerationin all object names =references, results.elements(); and initialize instance } maxInactiveInterval =((Integer) if( session == null) return; * information on the Apache Software Foundation, please see new HttpSessionBindingEvent(this,name); * * preparationwhile(names. forhasMoreElements reuse of this ())object.{ stream.readObjectisNew = ((Boolean)()).intValue stream.(); readObject ()).booleanValue(); if(session instanceofSession) * . * @ param manager The manager with which this */ Stringname = (String) names. nextElement(); * ((HttpSessionBindingListener)value).valueBound (e); Session is associated publicvoid removeAttributerecycle() { (name); /** ((Session) session).access(); * [Additional notices, if required by prior licensing conditions] } */ } * Remove the object bound with the specified name from thissession. If **/ values.put(name, value); public StandardSession(Manager manager) { Session // MarkReset this the sessioninstance as variables invalid associated with this *does thenothing. session does not have an object bound with this name,this method // cache the HttpSession -avoid another find } super(); setValidattributes.clear();(false); *

req.setSession ( session ); this.manager = manager; creationTime =0L; * After this method executes, and if the object implements /** } id = null; * HttpSessionBindingListener ,the container calls } package org.apache.tomcat.session; *@deprecated } manager = null; * valueUnbound () on the object. importorg.apache.tomcat.core.*; public*/ Object getValue(String name) { /** maxInactiveIntervalisNew = true; =-1; * @ param name Name of the object to remove from this session. importorg.apache.tomcat.util. StringManager; returngetAttribute (name); // ------* ReleaseisValid =all false; object references, and initialize instance * importjava. io.*; } ------Instance Variables variables,in *@exception IllegalStateException if this method is called on an importjava.net.*; * preparation// Tell our forManager reuse thatof this this object. Session has been recycled * invalidated session import java.util.*; publicObject getAttribute(String name) { */ if ((manager != null) && (manager instanceof * importjavax.servlet .*;.http.*; if(! Stringvalid) msg{ = sm.getString (" applicationSession.session.ise"); /*** The collection of user data attributes ManagerBasepublicvoid ))((ManagerBaserecycle() { ) manager).recycle(this); * @deprecated removeAttribute As of Version() 2.2, this method is replaced by associated with this Session. // Reset the instance variables associated with this */ /** thrownew IllegalStateException(msg); */ Session} publicvoid removeValue (String name) { * Core implementation of an application level session } privateHashtableattributes= new Hashtable(); attributes.clear(); * creationTime =0L; removeAttribute(name); * @author JasonJames HunterDuncan [ [email protected]] [ [email protected]] if String(name == msgnull)= { sm.getString (" applicationSession.value.iae"); /** Package// ------MethodsidlastAccessedTime = null; = 0L; Session } * @author James Todd [[email protected]] * The time this session was created, in manager= null; */ thrownew IllegalArgumentException (msg); milliseconds since midnight, maxInactiveInterval =-1; SessionInterceptor StandardManager StandardSessionManager } * January 1, 1970 GMT. /** isNew = true; /** publicclass ApplicationSession implementsHttpSession{ */ * ReturnisValid the= false;isValid flag for this session. * Bind an object to this session, using the specified name.If an object privateStringManagersm = } return values.get(name); private longcreationTime = 0L; boolean*/ // isValidTell our() Manager { that this Session has been recycled * replaced.of the same name is already bound to this session, the jectob is package org.apache.tomcat.request; StringManager.getManager ("org.apache.tomcat.session"); if ((manager != null) && (manager instanceof *

public int beforeBody ( Request rrequest, Response response ) { privateHashtablevalues = new Hashtable (); /** /** ManagerBasereturn)) (this.isValid); * After this method executes, and if the object implements importorg.apache.tomcat.core.*; StringreqSessionId =response. getSessionId (); private String id; *@deprecated *The session identifier of this Session. ((ManagerBase ) manager).recycle(this); * HttpSessionBindingListener ,the container calls importorg.apache.tomcat.util.*; java. io.*; if(reqSessionId debug>0 ) ==null)cm.log("Before Body " +reqSessionId ); package org.apache.tomcat.session; package org.apache.tomcat.session; // ------Public privateServerSession serverSession ; */ */ } * valueBound() on the object. importjava.net.*; return0; // ------Lifecycle Methods Methods private Context longcreationTime context;= System. currentTimeMillis ();; publicEnumeration String[] getValueNamese getAttributeNames= (){ (); private String id = null; } * @ param name Name to which the object is bound, cannot be null import java.util.*; importjava. io.IOException; private longthisAccessTime =creationTime ; Vector names = new Vector(); /** * @ param value Object to be bound, cannot be null importjavax.servlet .http.*; import java.util.Enumeration; importjava. io.IOException; privateprivateboolean longvalidlastAccessed = true; =creationTime ; /** // ------* Set the isNew flag for this session. Session * // GS, set the path attribute to the cookie. This way import java.util.Vector;java.util. Hashtable; /*** Configure this component, based on the specified configuration importjavax.servlet .http.Cookie; // XXX should we throw exception or just return null ?? privateintinactiveInterval =-1; while(e. hasMoreElements ()) { * Descriptive information describing this Package *Methods *@exception IllegalArgumentExceptionif an attempt is made to add a /*** Will process the request and determine the session Id, and tseit //context. multiple session cookies can be used, one for each importorg.apache.tomcat. catalina.*; * parameters. This method should be called immediately afterthe importjavax.servlet .http.HttpSession ; /**public HttpSessionfindSession (Context ctx, String id ) { privateApplicationSessionboolean valid(String = true;id, ServerSessionserverSession , names.addElement(e.nextElement()); Session implementation. * @ paramisNew The new value for the isNew * non-serializable object in an environment marked distributable. * in the Request. StringsessionPath=rrequest .getContext(). getPath (); importjavax.servlet .http.Cookie; * component instance is created, and before start() importorg.apache.tomcat. catalina.*; * Marktry { the specified session's last accessed time. This ouldsh be ApplicationSessionContextthis. serverSession context)(String {=serverSessionid, ServerSession; serverSession , } private*/ static final String info = flag/***/ * @exception invalidatedIllegalStateException session if this method is called on an * It also marks the session as accessed. if(sessionPath .length() == 0) { importjavax.servlet .http.HttpSession ; * is called. * calledSession for eachsession request = manager. byRequestInterceptor afindSession (id);. Contextthis.context context) = context; { String[]valueNames = new String[names.size()]; "StandardSession/1.0"; void* ReturnsetNew the(boolean isNew isValid) { flag for this session. */ * sessionPath="/"; importorg.apache.tomcat.util. StringManager; * import org.apache.tomcat.core.Context; this.this.idserverSession = id; =serverSession; */ publicvoid setAttribute(String name, Object value) { * This implementation only handles Cookies sessions, please extend or } import org.w3c.dom.Node;org.w3c.dom.NamedNodeMap; * @ (FIXME:param parameters What Configuration object type shouldparameters this for really this be?)component import org.apache.tomcat.core.Request; * if(session!=null) this.context = context; names.copyInto (valueNames); booleanthis. isValidisNew()=isNew {; * add new interceptors for other methods. // GS, piggyback thejvm route on the session id. * importorg.apache.tomcat.core.Response; * @ param sessionreturn The session. sessiongetSession to be marked(); this.idthis. inactiveInterval = id; = context.getSessionTimeOut(); /** if ((manager != null) && manager. getDistributable()&& */ if(!sessionPath.equals("/")) { *@exception IllegalStateException if this component has already been importorg.apache.tomcat.core. SessionManager ; */ } catch ( IOExceptione) { this.if(this. inactiveIntervalinactiveInterval= context.!= -1)getSessionTimeOut { (); returnvalueNames ; **/ The last accessed time for this Session. } return (this.isValid); !(valuethrowinstanceofnew IllegalArgumentException Serializable )) publicclass SessionInterceptor extends BaseInterceptor implements RequestInterceptor{ StringjvmRoute =rrequest.getJvmRoute(); /** * configured and/or started public} void accessed(Context ctx,Request req , String id) { this. inactiveInterval*= 60; } private longlastAccessedTime=creationTime; } (sm. getString ("standardSession .setAttribute.iae ")); if(null != jvmRoute){ * Standard implementation of the Manager interface thatprovides *@exception LifecycleException if this component detects a fatal error importorg.apache.tomcat.util. SessionUtil ; if}(this. inactiveInterval!= -1) { /** // GS, separates the session id from the jvm route reqSessionId=reqSessionId + SESSIONID_ROUTE_SEP + jvmRoute; * anno optional,session persistence configurable, or distributable maximum number capabilities, of active sessions but doesallowed.support **/ in the configuration parameters it was given HttpSessionreturn (null);session=findSession (ctx,id); } this. inactiveInterval*= 60; publicEnumeration getAttributeNames(){ /** * Set the isValid flag for this session. synchronized (attributes) { staticint debug=0; final char SESSIONID_ROUTE_SEP = '.'; }} *

publicvoid configure(Node parameters) } if( session == null) return; } if(! valid) { /*** The Manager with which this Session is /*** removeAttribute (name); ContextManagercm; * Lifecycle configuration of this component assumes an XML node throwsLifecycleException{ /** if(session instanceofSession) }ServerSessionreturnserverSession getServerSession; () { Stringmsg = sm.getString (" applicationSession.session.ise"); associated.**/ The Manager with which this Session is *isValid @ Setparam theisValid flag isNewThe new value flag for for the this session. attributes.put(name,if (valueinstanceof HttpSessionBindingListener value); ) Cookie cookie = new Cookie("JSESSIONID", * in the following format: public ((Session)HttpSession session).access();createSession (Context ctx){ ServerSession} getServerSession () { thrownew IllegalStateException(msg); private*/ Manager manager = null; **/ @ paramisNew The new value for the isNew ((HttpSessionBindingListener ) value). valueBound public SessionInterceptor () { reqSessionId); * // Validate and update our current component state * Specialized implementation of org.apache.tomcat.core. SessionManager returnserverSession; } private Manager manager = null; flagvoid setValid (booleanisValid ){ (new HttpSessionBindingEvent((HttpSession ) this, name)); } cookie. setMaxAge(-1); * & lt ;ManagercheckIntervalclassName="org.apache.tomcat.session.="60" maxActiveSessions="-1"StandardManager" if(configured) thrownew LifecycleException * that adapts to the new component-based Manager implementation. return manager.createSession ().getSession(); }/** */ } publicvoid setDebug(int i ) { cookie. setPathsetVersion(sessionPath(1); ); * maxInactiveInterval=" -1" /> (sm. getString ("standardManager .alreadyConfigured")); *

} // cache the HttpSession -avoid another find * Called by context when request comes in so that accessesand HashtablevaluesClone = Hashtable( )values.clone(); /** void setNewthis. isValid(boolean=isNew isValid); { System.out.println("Set debug to " + i); * configured= true; * XXX -At present, use of StandardManager is hard coded, req.setSession ( session ); /***inactivities*/ Called by contextcan be when dealt request with accordingly. comes in so that accessesand return (Enumeration)valuesClone .keys(); client/**requests * The maximumbefore time interval, in seconds, between } this. isNew=isNew; } debug=i; response. addHeader(CookieTools .getCookieHeaderName(cookie), * where you can adjust the following parameters, with defaultalues v if(parameters == null) }/** *inactivities can be dealt with accordingly. } clientrequests * the servletbefore container may invalidate this } CookieTools .getCookieHeaderValue(cookie)); * in square brackets: return; * and lifecycle configuration is not supported. */ session.* the A negativeservletcontainer time may invalidate this }// ------// ------HttpSession Private Methods cookie. setVersion (0); * ulli<>> checkInterval -The interval (in seconds) between background // Parse and process our configuration parameters *

down. * Remove all sessions because our associated Context is beingshut // HTTP SESSION IMPLEMENTATION METHODS session.* Aindicates negative thattime the session should never time HttpSessionProperties publicthis.cm=cm;void setContextManager(ContextManager cm ) { response. addHeader(CookieTools .getCookieHeaderNamegetCookieHeaderValue(cookie),(cookie)); * thread checks for expired sessions. [60] if(!("Manager".equals(parameters. getNodeName ()))) * IMPLEMENTATION NOTE: Once we commit to the new // XXX should we throw exception or just return null ?? void accessed() { /** out.* indicates that the session should never time } * li<> maxActiveSessions -The maximum number of sessions allowed to return; Manager/Session public* HttpSessionfindSession (Context ctx, String id ) { public//ifString (valid) fromset last { thegetId accessedprevious(){ to accessthisAccessTime as it will be left over **/ @deprecated out.private*/ intmaxInactiveInterval =- 1; /*** Set the isValid flag for this session. /*** Read a serialized version of this session object from thespecified return 0; * be active at once, or -1 for no limit. [-1] NamedNodeMap attributes = parameters.getAttributes (); * paradigm, I would suggest moving the logic implemented here ackb into * @ tryparam { ctx The context that is being shut down lastAccessedreturn id;=thisAccessTime ; privateintmaxInactiveInterval =- 1; * Return the time when this session was created, in * object input stream. public int requestMap (Request request ) { } * li<> maxInactiveInterval -The default maximum number of seconds of Node node = null; */ thisAccessTime} else { = System. currentTimeMillis (); publicvoid removeValue (String name) { milliseconds* @ param isValid since The new value for the *

StringsessionId = null; * inactivitya session, before or- 1 which for no the limit.servlet Thiscontainer value should is allowed be overridden to time from out node = attributes.getNamedItem("checkInterval "); * the core level. The Tomcat.Next "Manager" interface acts morelike a publicvoid SessionremoveSessions session = (Contextmanager.findSessionctx) { (id); Stringmsg = sm.getString (" applicationSession.session.ise"); removeAttribute(name); /** isValid* midnight, Januaryflag 1, 1970 GMT. * IMPLEMENTATION NOTE: The reference to the owningManager Cookie cookies[]=request.getCookies(); // assert !=null /** Notification of context shutdown * the default session timeout specified in the web application deployment if (node != null) { * collection class, and has minimal knowledge of the detailedequest r if(session!=null) validate(); } /*** Flag indicating whether this session is new or */* * is not restored by this method, and must be set explicitly. */ * descriptor, if any. [ -1] try{ * processing semantics of handling sessions. return session.getSession (); } }thrownew IllegalStateException(msg); publicvoid removeAttribute(String name) { not.**/ Flag indicating whether this session is new or calledvoid on*@exception setValidan (booleanIllegalStateExceptionisValid ){ ifthis method is * @ param stream The input stream to read from for( inti=0; i setCheckInterval (Integer. parseInt (node.getNodeValue())); *

}// catch XXX (XXXIOException a manager e)may {be shared by multiple void} validate() { if(! valid) { private*/ boolean isNew = true; * this.invalidatedisValid session=isValid; * Cookiecookie = cookies[i]; throwsTomcatException * } catch Throwable( t) { // contexts, we just want to remove the sessions ctxof ! // if we have an inactive interval, check to see if we've exceeded it Stringmsg = sm.getString (" applicationSession.session.ise"); privateboolean isNew = true; }*/ *@exception ClassNotFoundExceptionif an unknown class is specified { * @version@author $Revision:Craig R. 1.1.1.1McClanahan $ $Date: 2000/05/02 21:28:30 $ } ; // XXX -Throw exception? for* XXX -At present, there is no way (via theSessionManager interface) }// The manager will still run after that ( i.e. keep database publiciflong ( inactiveIntervalgetCreationTime!=() -1) { { publiclong getCreationTime() { *@exception IOException if an input/output error occurs if (cookie.sessionIdgetName=cookie. ().equals("JSESSIONID"))getValue(); { if(ctxctx.getSessionManager.getDebug() > 0 )(). ctxremoveSessions.log("Removing(ctx sessions); from " + ctx ); */ } return (null); if(valid) intthisInterval { = thrownew IllegalStateException(msg); /** */ sessionId =validateSessionId(request,sessionId); } * a Context to tell the Manager that we create what the defaultsession} // connection open } returnelse ({intcreationTime)(System. currentTimeMillis; () -lastAccessed ) / 1000; } or not./*** Flag indicating whether this session is valid HttpSession// ------returnProperties (this.creationTime); privatethrows voidClassNotFoundExceptionreadObject (ObjectInputStream,IOExceptionstream){ if( sessionId !=null){ public final classStandardManager node = attributes.getNamedItem("maxActiveSessions"); * timeout for this web application (specified in the deployment if(manager instanceofLifecycle){ ifString thisInterval(msg = sm.getString>inactiveInterval(" applicationSession) { .session.ise"); if (name == null) { or not.*/ } request.setRequestedSessionIdFromCookie(true); extendsManagerBase if (node != null) { descriptor) try{ invalidate(); Stringmsg = sm.getString (" applicationSession.value.iae"); private*/ boolean isValid = false; // Deserialize the scalar instance variables (except Manager) } } implements Lifecycle, Runnable { try{ setMaxActiveSessions(Integer.parseInt (node.getNodeValue())); * should be. public HttpSession((Lifecycle)createSession manager).stop();(Context ctx){ }thrownew IllegalStateException(msg); privateboolean isValid = false; /** creationTime =((Long) stream. readObject()).longValue (); }} } catch Throwable( t) { * return manager.createSession ().getSession(); } thrownew IllegalArgumentException (msg); /*** Return the time when this session was created, in id = (String) stream. readObject (); // ------Instance Variables ; // XXX -Throw exception? * @author Craig R. McClanahan } } catch LifecycleException( e){ } } /*** The string manager for this package. millisecondsassociated.* Returnmidnight, thesince January session 1, context 1970 GMT. with which this session is isValid lastAccessedTimemaxInactiveInterval= ((Boolean) stream.= ((Long)=readObject ((Integer) stream.()). stream.readObjectbooleanValuereadObject()).();longValue()).intValue(); (); Stringsig=";jsessionid="; } throw new IllegalStateException("" + e); ///** HTTP SESSION IMPLEMENTATION METHODS Object o = values.get(name); **/ The string manager for this package. * isNew// Deserialize= ((Boolean)the stream.attributereadObject count and()). attributebooleanValue values(); intfoundAt =-1; } */ } * private*/ StringManagersm = *@exception @deprecatedIllegalStateException As of Version 2.1, thisifthis methodmethod is isdeprecated isValidintn = ((Integer)= ((Boolean) stream. stream.readObject()).()).intValuebooleanValue();(); if( debug>0 ) cm.log(" XXX RURI=" + request.getRequestURI ()); /*** The interval (in seconds) between checks for expired sessions. node = attributes.getNamedItem("maxInactiveInterval"); /** } public*@deprecated String getId(){ if (o instanceof HttpSessionBindingListener ) { privateStringManagersm = calledand has onno an for ( inti= 0; i < n; i++) { if ((sessionIdfoundAt =request. getRequestURI().indexOfsubstring(sig())!=foundAt-1){+sig.length()); */ if (node != null) { public final classStandardSessionManager * Remove all sessions because our associated Context is beingshut */ if(valid) { HttpSessionBindingEvente= StringManager .getManager("org.apache.tomcat.session") * invalidatedreplacement. session It will be removed in a future version of // DeserializeStringname the= (String) attribute stream.count andreadObject attribute(); values // rewrite URL, do I need to do anything more? privateint checkInterval = 60; try{ implementsSessionManager { down. public} returnHttpSessionContextelse id;{ getSessionContext (){ new HttpSessionBindingEvent(this,name); StringManager; .getManager("org.apache.tomcat.session") the public*/* Javalong ServletgetCreationTimeAPI. () { intfor (nObjectattributes.put(name, =int ((Integer) valuei= 0; i =< stream.n; (Object)i++) { value);readObjectstream.readObject()).intValue(); (); request. setRequestURI(request.getRequestURI(). substring(0, foundAt)); setMaxInactiveInterval(Integer.parseInt(node. getNodeValue ())); }* returnString newmsgSessionContextImpl= sm.getString ("();applicationSession.session.ise"); ((HttpSessionBindingListener)o). valueUnbound (e); */ }Stringname = (String) stream. readObject(); sessionId=validateSessionId (request, sessionId ); } catch Throwable( t) { * @ paramctx The context that is being shut down } } /** publicreturnHttpSessionContext (this.creationTimegetSessionContext ); (){ Object value = (Object) stream.readObject (); if sessionId( !=null){ /***Has this component been configured yet? } ; // XXX -Throw exception? /***/ thrownew IllegalStateException(msg); /*** The HTTP session context associated with this } attributes.put(name, value); } request.setRequestedSessionIdFromURL(true); */ } // ------} values.remove(name); session.* The HTTP session context associated with this } if( sessionContext==null) } } privateboolean configured= false; Constructors timeout.public* Usedvoid by contextremoveSessions to configure(Context thectx session) { manager's inactivity } } session.private*/ static HttpSessionContext sessionContext returnsessionContext sessionContext( = );new StandardSessionContext(); }/** return 0; } publiclong getCreationTime() { publicvoid setMaxInactiveInterval(intinterval) { =null; privatestatic HttpSessionContext sessionContext /** * Write a serialized version of this session object to thespecified } *// XXX XXX a manager may be shared by multiple if(valid) { if(! valid) { =null; }* Return the session context with which this session is *object output stream. /*** The descriptive information about this implementation. * The// SessionManagercontexts, we justmay havewant someto remove default the session sessions time ctxof out,! the returncreationTime; Stringmsg = sm.getString (" applicationSession.session.ise"); associated. /***

// XXXWe may what still is the set correct it and behaviorjust return if thesession session invalid. is invalid? */ /** * Context on the other hand has it's timeout set by the deployment } else { /** * * IMPLEMENTATIONWrite a serialized NOTE:version of Thethis owning session Manager object will to thenotspecifiedbe stored private static final String info = StandardManager" /1.0"; /** *Create a new SessionManager that adapts to the corresponding *descriptor // The manager(web. xmlwill). Thisstill methodrun after lets that the ( Contexti.e. keepconforgure databasethe Stringmsg = sm.getString (" applicationSession.session.ise"); }thrownew IllegalStateException(msg); /****/ The current accessed time for this session. andHttpSession has// *------no @deprecatedPublicMethods As of Version 2.1, this method is deprecated *

objectin thereadObject output serializedstream. (), representation you must of set this the Session. associated After Managercalling /** Validate and fix the session id. If the session is not alidv return null. * Prepare for the beginning of active use of the public methods of this Manager // connection open thrownew IllegalStateException(msg); private*/ longthisAccessedTime=creationTime; * replacement. It will be removed in a future version of * explicitly.IMPLEMENTATION NOTE: The owning Manager will notbestored * It will also clean up the session from load -balancingstrings. * component. This method should be called after configure(), *implementation. * sessionif(manager managerinstanceof accordingLifecycle) to this{ value. } inactiveInterval = interval; private longthisAccessedTime=creationTime; the *

in the serialized representation of this Session. Aftercalling *@return sessionId, or null if not valid /*** The maximum number of active Sessions allowed, or- 1for no limit. * and before any of the public methods of the component areutilized. */ * try{ } } /*** Java Servlet API. * IMPLEMENTATIONreadObject(), NOTE: you Any mustattribute set the that associated is not Serializable Manager private*/ String validateSessionId(Requestrequest, String sessionId ){ */ *@exception IllegalStateException if this component has not yet been * @ param minutes The session inactivity timeout in minutes. */* Return the object bound with the specified name in this * explicitly.will be silently ignored. If you do not want any such tributes,at // GS, We piggyback the JVM id on top of the session cookie protected intmaxActiveSessions =- 1; * configured (if required for this component) public StandardSessionManager(){ */ ((Lifecycle) manager).stop(); /*** publicif(! intvalid) getMaxInactiveInterval{ (){ // ------session,public* null orHttpSessionContext if nogetSessionContext object is bound() with{ that name. *

IMPLEMENTATIONbeManager sure isthe set distributable to NOTE:true. Any attribute property that is notof ourSerializable sociatedas // Separate them ... *@exception IllegalStateException if this component has already been } catch LifecycleException( e){ *@deprecated Stringmsg = sm.getString (" applicationSession.session.ise"); ------// ------Session Properties *if( sessionContext==null) * will be silently ignored. If you do not want any such attributes, * started manager = newStandardManager (); publicvoid throwsetSessionTimeOut new IllegalStateException(int minutes) {("" + e); */ ------Session Properties * @ paramsessionContextname Name of the= newattributeStandardSessionContext to be returned(); * @ beparam surestream the Thedistributable output stream to write property to of our sociatedas if( debug>0 ) cm.log("Orig sessionId " +sessionId ); /*** The string manager for this package. *@exception that preventsLifecycleException this componentif from this being component used detects a fatal error if(manager instanceofLifecycle){ if(}-1 != minutes) { thrownew IllegalStateException(msg); *return sessionContext( ); * Manager is set to true. if int(null idex != sessionId=sessionId) { .lastIndexOf (SESSIONID_ROUTE_SEP); */ */ // The manager works with seconds... public HttpSessionContextgetSessionContext (){ } /** *@exception IllegalStateException ifthis method is *@exception IOException if an input/output error occurs if( idex > 0) { privateStringManagersm = public void start() throwsLifecycleException { try{ }manager. setMaxInactiveInterval(minutes * 60); } return newSessionContextImpl (); returninactiveInterval; method/** *is Set called the creation by the time for this session. This called} on* aninvalidated session private**/ @ param voidstreamwriteObject The output(ObjectOutputStream stream to writestream) to throwsIOException{ sessionId =sessionId .substring(0,idex); StringManager.getManager ("org.apache.tomcat.session"); ((Lifecycle) manager).configure(null); } method *is Manager called when by anthe existing Session instance is */ *@exception IOException if an input/output error occurs } // Validate and update our current component state ((Lifecycle) manager).start(); } } publiclong getLastAccessedTime(){ } reused.* Manager when an existing Session instance is //public ------Object getAttribute(String name) { */ // Write the scalar instance variables (except Manager) } /** if(!configured) thrownew LifecycleException } catch LifecycleException( e){ } if(valid) { reused.* HttpSessionPublicMethods privatestream. voidwriteObjectwriteObject(new( Long(ObjectOutputStreamcreationTimestream))); throwsIOException{ if( sessionId!= null && sessionId.length()!=0) { * Has this component been started yet? (sm. getString ("standardManager .notConfigured")); } returnlastAccessed; * @ param time The new creation time return (attributes.get(name)); stream. writeObject(id); // GS, We are in a problem here, we may actually get */ if(started) throw new IllegalStateException("" + e); /** } Stringelse {msg = sm.getString (" applicationSession.session.ise"); //------public**/ @ paramvoid timesetCreationTime The new creation(long time) time { /**} stream.writeObject//stream. WritewriteObject the(new scalarInteger( (new instanceBoolean( Long(maxInactiveIntervalcreationTime variablesisNew )); (except));)); Manager) // multiple Session cookies (one for the root privateboolean started = false; thrownew LifecycleException } * Used by context to configure the session manager's inactivity publicvoid setCreationTime(long time) { * Return the object bound with the specified name in this stream. writeObject(id);(newBoolean( isValid )); // context and one for the real context... or old ssionse (sm. getString ("standardManager .alreadyStarted ")); } timeout. thrownew IllegalStateException(msg); this. creationTime = time; session, or stream. writeObject(new Long(lastAccessedTime)); // cookie. We must check for validity in the currentcontext. /** started= true; * } this. creationTimethisAccessedTime= time;= time; /*** null if no object is bound with that name. stream.// AccumulatewriteObject the names(newInteger( of serializablemaxInactiveIntervalattributes )); ContextSessionManagerctx=request. sM =getContextctx.getSessionManager(); (); * The background thread. // Start the background reaper thread * The SessionManager may have some default session time out, the } this. lastAccessedTime = time; * Return an Enumeration of stream.Vector writeObjectresults = (newnewBoolean( Vector();isNew )); if(null != sM .findSession (ctx ,sessionId)){ */ threadStart(); } * Context on the other hand has it's timeout set by the deployment } this. thisAccessedTime = time; String* @ containingparam name Name objectsthe ofnames the ofattribute the objects to be bound returned to this stream.Enumerationwhile( writeObjectattrs attrs.hasMoreElements(new=getAttributeNamesBoolean( ()){ isValid();)); request. setRequestedSessionIdsM.accessed(ctx,(sessionId request,); sessionId ); private Thread thread = null; } session.*@exception IllegalStateException ifthis method is // StringAccumulateattr the= (String)names of serializableattrs.nextElementattributes(); request.if( debug>0setRequestedSessionId ) cm.log(" Final( sessionsessionId );id " sessionId+ ); } *descriptor (web. xml). This method lets the Contextconforgure the /** called on* an VectorObject results value = =new attributes.get( Vector(); attr ); if(return debug>0sessionId ) cm.log("; Final session id " sessionId+ ); /** // ------Instance * session manager according to this value. * Return the session identifier for this * @exception invalidatedIllegalStateException session ifthis method is Enumerationif (valueattrsinstanceof=getAttributeNamesSerializable ();) }} return sessionId ; * The background thread completion semaphore. /** Variables * session./** called on*/ an while( attrsresults..hasMoreElementsaddElement ())(attr { ); }return null; */ * Gracefully terminate the active use of the public methodsof this * @ param minutes The session inactivity timeout in minutes. session.public**/ ReturnString the sessiongetId(){ identifier for this public**/ Object invalidatedgetAttribute session (String name) { }StringObject attrvalue= (String)= attributes.get(attrs.nextElementattr ); (); } return null; privateboolean threadDone= false; * component. This method should be the last one called ona given */ publicreturnEnumeration (attributes.get(name));getAttributeNames(){ // ifSerialize (value theinstanceof attributeSerializable count and) the attribute values } *instance of this component. */ publicreturnString (this.id);getId(){ stream. results.writeObjectaddElement(new Integer(results.size()));(attr ); /** *@exception IllegalStateException if this component has not been started /** publicvoid setSessionTimeOut(int minutes) { } return (attributes.keys()); }Enumeration names = results.elements(); * Name to register for the background thread. *@exception IllegalStateException if this component has already * The Manager implementation we are actually using. if(-1 != minutes) { } return (this.id); while(names. hasMoreElements()){ */ * been stopped */ // The manager works with seconds... } /**} //stream. Stringstream.SerializewriteObjectname writeObject the= (String) (newattribute Integer(results.size()));(name);names. count nextElementand the attribute(); values privateString threadName = StandardManager" "; *@exception LifecycleException if this component detects a fatal error /** * Return an Enumeration of Enumerationstream.writeObject names = results.elements();(attributes.get(name)); * that needs to be reported private Manager manager = null; manager. setMaxInactiveInterval(minutes * 60); * Set the session identifier for this session. String/** objects while} (names. hasMoreElements()){ // ------Properties public*/ void stop() throws LifecycleException { } /*** * Returncontaining the object the names bound of with the the objects specified bound name to thisin this Stringname = (String) names. nextElement(); } * @ Setparam theid session The new identifier session identifierfor this session. session.session, or stream.writeObject(name); // Validate and update our current component state } public**/ @ paramvoid id setIdThe new(String session id) {identifier *@exception nullIllegalStateException if no object isif this boundmethod with thatis name. } }stream.writeObject(attributes.get(name)); /** if(!started) */ called on* @ paraman name Name of the value to be returned crosscut invalidate(StandardSession s): s & ( int getMaxInactiveInterval ()| * Return the check interval (in seconds) for this Manager. thrownew LifecycleException publicifvoid ((this.idsetId(String != null) id) && { (manager != null) && * invalidated session long getCreationTime()| public*/ int getCheckInterval() { started =(sm. false;getString ("standardManager .notStarted")); (managerinstanceof ManagerBase )) */*@exception IllegalStateException ifthis method is } Object getAttribute (String) | if ((((this.idManagerBase != null)) manager).remove(this); && (manager != null) && calledpublic on anEnumeration getAttributeNames(){ Enumeration getAttributeNames()| return (this.checkInterval ); // Stop the background reaper thread ServerSession this.id(manager((ManagerBase =instanceof id; ) ManagerBasemanager).remove(this);)) * returninvalidated (attributes.keys()); session crosscut invalidate(StandardSession s): s & ( intlongString[]void invalidate getCreationTimegetMaxInactiveIntervalgetValueNames()| ()| ()| ()| threadStop(); * @deprecated As of Version 2.2, this method is replaced ObjectbooleangetAttributeisNew () |(String) | } this.idif ((manager = id; != null) && (manager instanceof by} Enumerationvoid removeAttributegetAttributeNames(String) |()| //Session Expire sessions[]all active sessions=findSessions (); ManagerBase)) * getAttribute() String[]void setAttributegetValueNames(String,()| Object)); /** for ( inti = 0; i < sessions.length; i++) { if ((manager((ManagerBase != null)) manager).add(this); && (manager instanceof */ voidinvalidate ()| * Set the check interval (in seconds) for this Manager. StandardSession session = ( StandardSession) sessions[i]; ManagerBase} ))((ManagerBase ) manager).add(this); /**public* ReturnObject the getValueobject bound(String with name) the {specified name in this staticbeforeadvice( {StandardSessions):invalidate(s) booleanvoid{ removeAttributeisNew () |(String) | * if (!session. isValid ()) session,return or getAttribute( (name)); if (!s.isValid()) void setAttribute(String, Object)); * @ paramcheckInterval The new check interval continue; package org.apache.tomcat.session; voidsynchronized validate() void { invalidate() { } * null if no object is bound with that name. throw new IllegalStateException public*/ void setCheckInterval (int checkInterval){ }session.expire(); //Enumeration if we haveenum an= inactiveappSessions interval,.keys(); check to see if /** }* staticadvice( StandardSession(s.sm.getStrings):(" standardSessioninvalidate(s) { ." importorg.apache.tomcat.core.*; // we've exceeded it * Return descriptive information about this * @ param name Name of the value to be returned before { +thisJoinPoint .methodName ServerSessionManager this. checkInterval=checkInterval; } importorg.apache.tomcat.util. StringManager; while( enum.hasMoreElements()) { Session/*** Returnimplementationthe corresponding descriptive andversion information number, about in thisthe /***@exception IllegalStateException ifthis method is }if (!s.throwisValid new ())IllegalStateException+ ".ise")); importjava. java.net.*; io.*; if( inactiveIntervalintObjectApplicationSessionthisInterval key = enum appSession!==.nextElement -1){ ();= Sessionformat implementation and called on* Returnan the set of names of objects bound to this } (s.sm.getString(" standardSession." } import java.util.*; (intApplicationSession)(System. currentTimeMillis)appSessions().get(key); -lastAccessed ) / 1000; * the corresponding version number, in the session.* invalidatedIf there session +thisJoinPoint .methodName // ------Private Methods importjavax.servlet .*; format<description& gt;/& lt ;version>. *are no such objects, a zero -length array is returned. + ".ise")); /** importjavax.servlet .http.*; ifappSession thisInterval( .invalidate();>inactiveInterval) { **/ * @deprecated As of Version 2.2, this method is replaced } * Return descriptive information about this Manager implementationand /** } invalidate(); &publiclt*/ ;description&String getInfogt;/&(){ lt ;version>. bycalled on*@exception angetAttributeIllegalStateException() ifthis method is } } // XXX * the corresponding version number, in the format /*** Invalidate all sessions that have expired. /*** Core implementation of a server session } ServerSessionManager ssm= publicreturnString (this.info);getInfo (){ */* invalidated session package org.apache.tomcat.session; //sync'd for safty--no other thread should be getting something *& lt;description& gt;/& lt;version>. */* Sleep for the duration specified by the checkInterval * publicvoid putValueServerSessionManager(String name, Object.getManager value) {(); public* Object getValue(String name) { // from this while we are reaping. This isn't the most optimal public*/ String getInfo (){ private**/ property. voidprocessExpires() { * @author James Duncan Davidson [ [email protected]] if (name == null) { } return (this.info); * @deprecated As of Version 2.2, this method is replaced // ------PrivateClass importorg.apache.tomcat.util.*; // solution for this, but we'll determine something else later. privatelong voidtimeNowthreadSleep=System. (){ currentTimeMillis(); * @author James Todd [[email protected]] Stringssm.msgremoveSession= sm.getString(this);(" serverSession.value.iae"); by return getAttribute( (name)); } importorg.apache.tomcat.core.*; java. io.*; synchronized void reap() { return (this.info); Session sessions[] =findSessions (); */ } }/** }**/ getAttributeNames() /** importjava.net.*; Enumeration enum = sessions.keys(); try { publicclass ServerSession{ } }thrownew IllegalArgumentException (msg); * Return the Manager within which this Session public String[] getValueNames(){ // * This------class is a dummy implementation of the HttpSessionContextPrivateClass import java.util.*; } for (Thread.sleep(inti = 0; i < sessions.length;checkInterval* 1000L); i++) { is valid./** * interface, to conform to the requirement that such an objectbe returned importjavax.servlet .http.*; while( enum.hasMoreElements()) { } catchStandardSessionif; (!session.( InterruptedExceptionisValidsession()) = (e)StandardSession { ) sessions[i]; privateStringManagersm = synchronizedremoveValue void(name); invalidate() // remove { any existing binding **/ Return the last time the client sent a request /** Vector results = new Vector(); * when HttpSession .getSessionContext() is called. Objectkey = enum .nextElement (); /** } continue; StringManager.getManager ("org.apache.tomcat.session"); Enumerationvalues.put(name,enum value);=appSessions.keys(); associatedpublic withManager thisgetManager () { * ReturnEnumeration the setattrs of names=getAttributeNames of objects bound(); to this /*** /*** ServerSession session = ( ServerSession)sessions.get(key); * Return the maximum number of active Sessions allowed, or- 1 for intmaxInactiveInterval = session. getMaxInactiveInterval (); privateHashtablevalues = new Hashtable (); } midnight,* session,returnJanuary 1, (this.manager);as 1970 the number of milliseconds since session.*are while no StringIf( such thereattrsobjects, attr.hasMoreElements= (String)a zero -length())attrs{ . arraynextElement is returned.(); * Thisinterface,@author class Craig isto a conform dummyR. McClanahan implementationto the requirement of the that suchHttpSessionContext an objectbe returned * @author James Duncan Davidson [ [email protected]] session.reap(); * no limit. } if maxInactiveInterval( < 0) privateHashtable String id; appSessions= new Hashtable (); publicwhileObject Object( enumkey .getValuehasMoreElements= enum(String.nextElement name)()) { {(); * GMT. Actions that your application takes, * results. addElement(attr); * @deprecatedwhen HttpSessionAs of Java .getSessionContextServletAPI 2.1 with() no replacement. is called. The * @author Jason Hunter [ [email protected]] session.validate(); */ continue; private longcreationTime = System. currentTimeMillis ();; if ApplicationSession(name == null) { appSession = such }as getting or setting *@exception } IllegalStateException ifthis method is * interface will be removed in a future version of this API. * @author James Todd [[email protected]] } public int getMaxActiveSessions(){ /** inttimeIdle (int ) timeNow(( = // Truncate,-session. do getLastAccessedTimenot round up ()) / 1000L); private longthisAccessTime =creationTime ; String(ApplicationSessionmsg = sm.getString)appSessions(" serverSession.get(key);.value.iae"); * a value associated with the session, do not called on Stringan names[] = new String[results.size()]; **/ @author Craig R. McClanahan */ } return (this.maxActiveSessions); * Startif thetimeIdle( background>= maxInactiveInterval thread that will periodically) checkfor privateprivate intinactiveInterval longlastAccessed =- creationTime1; ; affect the access time. * forinvalidated ( inti = session0; i < names.length; i++) * publicclass ServerSessionManagerimplements SessionManager{ synchronized voidremoveSession(ServerSession session) { * session timeouts.session.expire(); privateintinactiveInterval =-1; appSessionthrownew .invalidate();IllegalArgumentException (msg); public/***/* Setlong the getLastAccessedTimeManager within which(){ this Session is * @deprecatedreturnnames[i] (names); As= (String)of Version results. 2.2, thiselementAt method(i); is replaced final* @deprecatedinterface classStandardSessionContext Aswill of Javabe removedServlet in aAPIimplements future 2.1 with version noHttpSessionContext replacement. of this API. The{ String id = session.getId(); } */ } ServerSessionthis.id =(String id; id) { } } valid. by */ privateStringManagersm = }private voidthreadStart(){ } this.id = id; return values.get(name); *return (this.lastAccessedTime ); }* getAttributeNames() private Vector dummy = new Vector(); StringManager.getManager ("org.apache.tomcat.session"); session.invalidate(); /** if (thread != null) } public} void putValue(String name, Object value) { * @ param manager The new Manager */ final classStandardSessionContext implementsHttpSessionContext{ privatestatic ServerSessionManager manager; // = newServerSessionManager(); sessions.remove(id); * Set the maximum number of actives Sessions allowed, or- 1 for /** return; publicString getId(){ if (name == null) { }*/ public String[] getValueNames(){ /** protected int inactiveInterval= -1; } * no limit. * Sleep for the duration specified by the checkInterval publicreturnString id;getId(){ publicEnumeration Stringmsg =getValueNames sm.getString()(" serverSession{ .value.iae"); publicvoid setManager(Manager manager) { /*** InvalidatesVector results this = sessionnew Vector(); and unbinds any objects bound private*within Return Vectorthis the context.session dummy identifiers= new Vector(); of all sessions defined publicvoid removeSessions(Context context) { * *property. threadDone= false; } return id; } returnthrow values.keys();new IllegalArgumentException (msg); /** this.manager = manager; to it.Enumeration attrs =getAttributeNames(); * static { Enumeration enum = sessions.keys(); * @ param max The new maximum number of sessions */ thread = new Thread(this,threadName ); publiclong getCreationTime() { } * Return the Manager within which this Session *while( attrs .hasMoreElements()){ /*** @deprecated As of Java Servlet API 2.1 with no replacement. manager = newServerSessionManager(); public*/ void setMaxActiveSessions(intmax) { privatethread.thread.start(); voidsetDaemonthreadSleep(true);(){ publicreturnlong getCreationTimecreationTime ;() { publicvoid removeValue (String name) { is valid.} *@exception StringattrIllegalStateException= (String) attrs.ifthis nextElementmethod ();is * Return This the method session must identifiers return an of emptyall sessions Enumeration } while( enum.hasMoreElements()) { try { } returncreationTime ; removeValuevalues.remove(name);(name); // remove any existing binding */ calledon results. addElement(attr); *within and willthis context.be removed in a future version of the API. publicstatic ServerSessionManager getManager (){ ObjectServerSessionkey = enumsession.nextElement = ( ServerSession(); )sessions.get(key); this. maxActiveSessions=max; } Thread.sleep( checkInterval * 1000L); } } values.put(name, value); public/** Manager getManager () { **/ an}String invalidated names[] session= new String[results.size()]; public**/ @deprecatedEnumeration As ofgetIds Java Servlet() { API 2.1 with no replacement. return manager; ApplicationSession appSession = } catch ( InterruptedException e) { publiclong ApplicationSessiongetLastAccessedTimegetApplicationSession (){ (Contextcontext, }publicvoid setMaxInactiveInterval(intinterval) { * Returnreturn the (this.manager); maximum time interval, in seconds, publicfor ( voidint invalidate()i= 0; i < names.length; { i++) * This method must return an empty Enumeration } session.getApplicationSession(context,false); } ; returnboolean lastAccessedcreate){ ; publicinactiveIntervalObject getValue(String= interval; name) { between client requests names[i] = (String) results.elementAt (i); * returnand will (dummy.elements()); be removed in a future version of the API. /*** Stop} the background thread that is periodically checkingfor } ApplicationSession appSession = } if (name == null) { }*before the servlet container will invalidate return// Cause this(names); session to expire */ privateHashtablesessions= new Hashtable (); if appSession( !=null) { // ------PublicMethods }* session timeouts. (ApplicationSession)appSessions.get(context); Stringmsg = sm.getString (" serverSession.value.iae"); the session. A negative expire(); public} Enumeration getIds () { privateReaper reaper; } appSession.invalidate(); */ public ApplicationSessiongetApplicationSession (Contextcontext, public int getMaxInactiveInterval (){ time /**out.* time indicates that the session should never } return (dummy.elements()); privateServerSessionManager (){ } private voidthreadStop (){ booleanApplicationSessionif( appSessioncreate){ == nullappSession && create) = { } }return throwinactiveIntervalnew IllegalArgumentException; (msg); * Set the Manager within which this Session is /** reaper = Reaper.getReaper(); } /** /** (//ApplicationSession XXX )appSessions.get(context); valid.*@exception IllegalStateException ifthis /** }* Return the HttpSessionassociated with the reaper. setServerSessionManager(this); * Constructsettings specified and return by a thisnew sessionManager's object, properties. based onThe the sesefaultsion d * sessionStartif return;(thread thetimeouts. ==background null) thread that will periodically checkfor // sync to ensure valid? //XXX return values.get(name); method is* called on /*** Invalidates this session and unbinds any objects bound * specified session identifier. reaper.start(); /** * id will be assigned by this method, and available via thegetId () */ if( appSession == null && create) { }//sync'd for safty--no other thread should be getting something * @ anparam invalidatedmanager The sessionnew Manager to it.* Return true if the client does not yet know * } * Used by context to configure the session manager's inactivitytimeout. * method of the returned session. If a new session cannotbe created privatethreadDone voidthreadStart= true;(){ appSession = newApplicationSession(id, this, context); // from this while we are reaping. This isn't the most optimal public*/ void int setManagergetMaxInactiveInterval(Manager manager)(){ { about*the @exception session, orIllegalStateException if the client choosesif thisnot tomethod join isthe /*** @ Returnparam id the Session HttpSession identifier for whichassociated to look with up a thesession publicpublic HttpSessionvoid accessed( createSession Context (Contextctx , Requestctx ) req{, String id ) { * The SessionManager may have some default session time out, the * for any reason, return null. thread.interrupt(); }//appSessions syncXXX to ensure.put(context, valid? appSession); public// solutionreturnEnumeration values.keys();for this,getValueNames but we'll() determine { something else later. calledsession. on For * @deprecatedspecified sessionAs of Javaidentifier.Servlet API 2.1 with no replacement. ApplicationSessionStringsessionId = SessionIdGeneratorapS=( ApplicationSession.generateId)findSession();(ctx,id); * Context on the other hand has it's timeout set by the deployment * iftry {(thread != null) }synchronized void reap() { this.managerreturn (this.maxInactiveInterval = manager; ); * example, an invalidated if the server session used only cookie -based sessions, * This method must return null and will be removed in a if(ServerSessionapS ==null)sessionreturn; = newServerSession (sessionId); *descriptor (web. xml). This method lets the Contextconforgure the *@exception instantiatedIllegalStateException for any reason if a new session cannot be } catchreturn;thread.join(); ( InterruptedException e) { //appSession XXX = newApplicationSession(id, this, context); Enumeration enum =appSessions.keys(); and the*/ client * @ paramfutureid version Session of identifierthe API. for which to look up a session sessions.put(sessionId, session); * session manager according to this value. */ threadDone; = false; // appSessionsmake sure that.put(context, we haven't goneappSession over the); end of our publicvoid removeValue (String name) { } public* has voiddisabled invalidate() the use of { cookies, then a session would be **/ ServerSessionservSif(-1.accessed(); !=inactiveInterval servS =apS.getServerSession){ (); * @ param minutes The session inactivity timeout in minutes. publicSession createSession (){ thread} = new Thread(this,threadName ); }// inactive interval -- if so, invalidate and create values.remove(name);while( enum.hasMoreElements()) { new on*request. each// Cause this session to expire public* @deprecated ThisHttpSession method As mustof getSession Javareturn Servletnull(String andAPI id)will 2.1 { be with removed no inreplacement. a apSsession..accessed();setMaxInactiveInterval(inactiveInterval); */ thread. setDaemon(true); // XXXa newappSession } ObjectApplicationSessionkey = enum appSession.nextElement ();= /** *expire(); * returnfuture version(null); of the API. } publicvoid setSessionTimeOut(int minutes) { if (( maxActiveSessions>= 0) && thread.start();thread = null; //return make appSessionsure that ;we haven't gone over the end of our publicvoid setMaxInactiveInterval(ApplicationSession)appSessions(intinterval).get(key); { * ReturnSet the the maximum maximum time time interval, interval, in in seconds,seconds, *@exception IllegalStateException ifthis method is */ //return cache itsession. -nogetApplicationSession need to compute it again(ctx , true ); if(-1 != minutes) { (sessions.size()thrownew IllegalStateException >= maxActiveSessions)) } } // inactive interval -- if so, invalidate and create inactiveInterval = interval; between client requests called} on an public} HttpSessiongetSession (String id) { } req.setSession (apS ); // The manager works with seconds... (sm. getString ("standardManager .createSession.ise")); // a newappSession } appSession .validate(); *before the servlet container will invalidate * invalidated session }public HttpSessionfindSession (Contextctx ,String id) { }inactiveInterval= (minutes * 60); void removeApplicationSession(Context context) { } the session.* time A indicatesnegative that the session should never /**public*/ booleanisNew () { } return (null); publicServerSessionHttpSession createSessionsSession=( ServerSession(Context ctx)sessions.get(id);){ } return (super. createSession()); /**// ------Background Thread } returnappSessionsappSession.remove(context);; } public} returnint inactiveIntervalgetMaxInactiveInterval; (){ time out. * Return true if the client does not yet know } Stringif(sSessionsessionId==null) =returnSessionIdGenerator null; .generateId (); } * Stop the background thread that is periodically checkingfor } * aboutthe return (this.isNew); ServerSessionsession = newServerSession (sessionId); } /****/ session timeouts. void/** removeApplicationSession(Context context) { * @ @exception param intervalIllegalStateException The new maximum ifintervalthis * session, or if the client chooses not to join the } sessions.put(returnsSessionsessionId.getApplicationSession, session); (ctx , false); private* The background voidthreadStop thread(){ that checks for session timeouts danshutdown. * CalledappSessions by .remove(context);context when request comes in so that accessesand //XXX method is*/ called on session.} For } if(-1 !=inactiveInterval){ */ }*inactivities can be dealt with accordingly. //sync'd for safty--no other thread should be getting something { public**/ anvoid invalidatedsetMaxInactiveInterval session (intinterval) and the* example,client if the server used only cookie -based sessions, session.setMaxInactiveInterval(inactiveInterval); publicif (threadvoid run() == null) { /***/ // fromsolution this for while this, we arebut reaping.we'll determine This isn't something the most else optim lataler. public int getMaxInactiveInterval (){ * has disabled the use of cookies, then a session would be } return; * Called by context when request comes in so that accessesand this. maxInactiveInterval =interval; new on each return session.getApplicationSession (ctx , true ); threadDone//while Loop(! threadDone until= true; the) { termination semaphore is set void*inactivities validate() can be dealt with accordingly. synchronized void reap() { return (this.maxInactiveInterval); *request. } thread.interrupt();threadSleep(); */ Enumeration enum =appSessions.keys(); } * public HttpSessionfindSession (Contextctx ,String id) { try {processExpires(); } called on*@exception an IllegalStateException ifthis method is ServerSession sSession=( ServerSession)sessions.get(id); }thread.join(); void //accessed() set last accessed{ to thisAccessTime as it will be left over whileObject( enumkey .hasMoreElements= enum .nextElement()) {(); * invalidated session if(sSession==null) return null; } catch ( InterruptedException e) { // from the previous access ApplicationSession appSession = /** */ } }; (ApplicationSession)appSessions.get(key); * Set the maximum time interval, in seconds, public booleanisNew () { returnsSession.getApplicationSession(ctx , false); lastAccessed =thisAccessTime ; between client requests } } thread = null; thisAccessTime = System. currentTimeMillis (); appSession .validate(); the session.*before Athe negativeservlet container will invalidate return (this.isNew); } } } * time indicates that the session should never } } } time out. void validate() * // ------Background Thread * @ param interval The new maximum interval public*/ void setMaxInactiveInterval(intinterval) { /** **/ The background thread that checks for session timeouts danshutdown. this. maxInactiveInterval =interval; public void run() { } // Loop until the termination semaphore is set while(! threadDone){ threadSleepprocessExpires();(); } 9 } AspectJ Tutorial }

modular aspects

HTTPRequest SessionInterceptor getCookies () getRequestURI()(doc) requestMap(request) getSession() beforeBody(req, resp) getRequestedSessionId() ......

Session HTTPResponse getAttribute(name) getRequest() setAttribute(name, val) setContentType(contentType) invalidate() getOutptutStream() ... setSessionId(id) ...

Servlet 10 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 5 Aspect-oriented programming with AspectJ

AspectJ™ is…

• a small and well-integrated extension to Java • a general-purpose AO language – just as Java is a general-purpose OO language • freely available implementation – compiler is Open Source • includes IDE support – , JBuilder, Forte • user feedback is driving language design – users@.org – [email protected] • currently at 1.0 release

11 AspectJ Tutorial

expected benefits of using AOP

• good modularity, even for crosscutting concerns – less tangled code – more natural code – shorter code – easier maintenance and evolution • easier to reason about, debug, change – more reusable • library aspects • plug and play aspects when appropriate

12 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 6 Aspect-oriented programming with AspectJ

outline

• I AOP overview – brief motivation, essence of AOP idea • II AspectJ language mechanisms – basic concepts, language semantics • III development environment – IDE support, running the compiler, debugging etc. • IV using aspects – aspect examples, how to use AspectJ to program aspects, exercises to solidify the ideas • V related work – survey of other activities in the AOP community

13 AspectJ Tutorial

looking ahead

problem structure Part IV: crosscutting in the design, and how to use AspectJ to capture that

AspectJ mechanisms Part II: crosscutting in the code mechanisms AspectJ provides

14 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 7 Aspect-oriented programming with AspectJ

Part II

Basic Mechanisms of AspectJ

goals of this chapter

• present basic language mechanisms – using one simple example • emphasis on what the mechanisms do • small scale motivation • later chapters elaborate on – environment, tools – larger examples, design and SE issues

16 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 8 Aspect-oriented programming with AspectJ

basic mechanisms

• 1 overlay onto Java – join points • “points in the execution” of Java programs • 4 small additions to Java – • pick out join points and values at those points – primitive pointcuts – user-defined pointcuts – • additional action to take at join points in a – introduction • additional fields/methods/constructors for classes – aspect • a crosscutting type – comprised of advice, introduction, field,constructor and method declarations

17 AspectJ Tutorial

a simple figure editor

class Line implements FigureElement{ private Point p1, p2; display must be updated when Point getP1() { return p1; } Point getP2() { return p2; } objects move

void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } } class Point implements FigureElement { private int x = 0, y = 0;

int getX() { return x; } int getY() { return y; }

void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } } 18 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 9 Aspect-oriented programming with AspectJ

move tracking

• collection of figure elements – that change periodically … – must monitor changes to refresh the display as needed – collection can be complex • hierarchical • asynchronous events

• other examples – session liveness – value caching

19 AspectJ Tutorial

join points key points in dynamic call graph

a method is called a Figure and returns or throws dispatch a method is called and returns or throws a Line

dispatch a method executes and a returnsor throws

20 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 10 Aspect-oriented programming with AspectJ

terminology key points in dynamic call graph

a Line

dispatch

method execution join points

method call • several kinds of join points join points – method & constructor call join points – method & constructor execution join points – field get & set join points – exception handler execution join points – static & dynamic initialization join points

21 AspectJ Tutorial

join point terminology key points in dynamic call graph

all join points on this slide are within the control flow of this join point a Line a Point

a Figure

repeated calls to a Point the same method on the same object result in multiple join points

22 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 11 Aspect-oriented programming with AspectJ

the pointcut construct names certain join points

each time a Line receives a “void setP1(Point)” or “void setP2(Point)” method call

name and parameters a “void Line.setP1(Point)” call pointcut move(): or call(void Line.setP1(Point)) || call(void Line.setP2(Point));

a “void Line.setP2(Point)” call

23 AspectJ Tutorial

pointcut designators

user-defined pointcut designator

pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point));

primitive pointcut designator, can also be: - call, execution - this, target - get, set - within, withincode - handler - cflow, cflowbelow - initialization, staticinitialization

24 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 12 Aspect-oriented programming with AspectJ

after advice action to take after computation under join points

after advice runs “on the way back out” a Line

pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point));

after() returning: move() { }

25 AspectJ Tutorial

a simple aspect MoveTracking v1

aspect MoveTracking { an aspect defines a private boolean flag = false; special class that can public boolean testAndClear() { boolean result = flag; crosscut other classes flag = false; return result; }

pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point));

after() returning: move() { flag = true; } }

box means complete running code 26 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 13 Aspect-oriented programming with AspectJ

without AspectJ MoveTracking v1

class Line { class MoveTracking { private Point p1, p2; private static boolean flag = false;

Point getP1() { return p1; } public static void setFlag() { Point getP2() { return p2; } flag = true; } void setP1(Point p1) { this.p1 = p1; public static boolean testAndClear() { MoveTracking.setFlag(); boolean result = flag; } flag = false; void setP2(Point p2) { return result; this.p2 = p2; } MoveTracking.setFlag(); } } }

• what you would expect – calls that set the flag are tangled through the code – “what is going on” is less explicit

27 AspectJ Tutorial

the pointcut construct can cut across multiple classes

pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int));

28 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 14 Aspect-oriented programming with AspectJ

a multi-class aspect MoveTracking v2

aspect MoveTracking { private boolean flag = false; public boolean testAndClear() { boolean result = flag; flag = false; return result; }

pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int));

after() returning: move() { flag = true; } }

29 AspectJ Tutorial

using context in advice demonstrate first, explain in detail afterwards

• pointcut can explicitly expose certain values • advice can use value

pointcut move(FigureElement figElt): target(figElt) && (call(void Line.setP1(Point)) || parameter call(void Line.setP2(Point)) || mechanism is call(void Point.setX(int)) || being used call(void Point.setY(int)));

after(FigureElement fe) returning: move(fe) { }

30 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 15 Aspect-oriented programming with AspectJ

context & multiple classes MoveTracking v3

aspect MoveTracking { private Set movees = new HashSet(); public Set getMovees() { Set result = movees; movees = new HashSet(); return result; }

pointcut move(FigureElement figElt): target(figElt) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)));

after(FigureElement fe) returning: move(fe) { movees.add(fe); } } 31 AspectJ Tutorial

parameters… of user-defined pointcut designator

• variable bound in user-defined pointcut designator • variable in place of type name in pointcut designator – pulls corresponding value out of join points – makes value accessible on pointcut pointcut parameters pointcut move(Line l): target(l) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point))); typed variable in place of type name

after(Line line): move(line) { } 32 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 16 Aspect-oriented programming with AspectJ

parameters… of advice

• variable bound in advice • variable in place of type name in pointcut designator – pulls corresponding value out of join points – makes value accessible within advice

pointcut move(Line l): target(l) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point))); typed variable in place advice parameters of type name

after(Line line): move(line) { } 33 AspectJ Tutorial

parameters…

• value is ‘pulled’ – right to left across ‘:’ left side : right side – from pointcut designators to user-defined pointcut designators – from pointcut to advice

pointcut moves(Line l): target(l) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)));

after(Line line): move(line) { }

34 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 17 Aspect-oriented programming with AspectJ

target primitive pointcut designator

target()

any join point at which target object is an instance of type (or class) name

target(Point) target(Line) target(FigureElement)

“any join point” means it matches join points of all kinds • method & constructor call join points • method & constructor execution join points • field get & set join points • exception handler execution join points • static & dynamic initialization join points

35 AspectJ Tutorial

an idiom for… getting object in a polymorphic pointcut

target() &&

• does not further restrict the join points • does pick up the target object

pointcut move(FigureElement figElt): target(figElt) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)));

after(FigureElement fe): move(fe) { }

36 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 18 Aspect-oriented programming with AspectJ

context & multiple classes MoveTracking v3

aspect MoveTracking { private Set movees = new HashSet(); public Set getMovees() { Set result = movees; movees = new HashSet(); return result; }

pointcut move(FigureElement figElt): target(figElt) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)));

after(FigureElement fe): move(fe) { movees.add(fe); } } 37 AspectJ Tutorial

without AspectJ

class Line { private Point p1, p2;

Point getP1() { return p1; } Point getP2() { return p2; }

void setP1(Point p1) { this.p1 = p1;

} void setP2(Point p2) { this.p2 = p2;

} }

class Point { private int x = 0, y= 0;

int getX() { return x; } int getY() { return y; }

void setX(int x) { this.x = x;

} void setY(int y) { this.y = y;

} } 38 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 19 Aspect-oriented programming with AspectJ

without AspectJ MoveTracking v1

class Line { class MoveTracking { private Point p1, p2; private static boolean flag = false;

Point getP1() { return _p1; } public static void setFlag() { Point getP2() { return _p2; } flag = true; } void setP1(Point p1) { this.p1 = p1; public static boolean testAndClear() { MoveTracking.setFlag(); boolean result = flag; } flag = false; void setP2(Point p2) { return result; this.p2 = p2; } MoveTracking.setFlag(); } } }

class Point { private int x = 0, y= 0;

int getX() { return x; } int getY() { return y; }

void setX(int x) { this.x = x;

} void setY(int y) { this.y = y;

} } 39 AspectJ Tutorial

without AspectJ MoveTracking v2

class Line { class MoveTracking { private Point p1, p2; private static boolean flag = false;

Point getP1() { return p1; } public static void setFlag() { Point getP2() { return p2; } flag = true; } void setP1(Point p1) { this.p1 = p1; public static boolean testAndClear() { MoveTracking.setFlag(); boolean result = flag; } flag = false; void setP2(Point p2) { return result; this.p2 = p2; } MoveTracking.setFlag(); } } }

class Point { private int x = 0, y = 0;

int getX() { return x; } int getY() { return y; }

void setX(int x) { this.x = x; MoveTracking.setFlag(); } void setY(int y) { this.y = y; MoveTracking.setFlag(); } } 40 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 20 Aspect-oriented programming with AspectJ

without AspectJ MoveTracking v3

class Line { class MoveTracking { private Point p1, p2; private static Set movees = new HashSet();

Point getP1() { return p1; } public static void collectOne(Object o) { Point getP2() { return p2; } movees.add(o); } void setP1(Point p1) { this.p1 = p1; public static Set getmovees() { MoveTracking.collectOne(this); Set result = movees; } movees = new HashSet(); void setP2(Point p2) { return result; this.p2 = p2; } MoveTracking.collectOne(this); } } }

class Point { private int x = 0, y = 0; • evolution is cumbersome int getX() { return x; } int getY() { return y; } – changes in all three classes

void setX(int x) { this.x = x; – have to track all callers MoveTracking.collectOne(this); } • change method name void setY(int y) { this.y = y; • add argument MoveTracking.collectOne(this); } } 41 AspectJ Tutorial

with AspectJ

class Line { private Point p1, p2;

Point getP1() { return p1; } Point getP2() { return p2; }

void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } }

class Point { private int x = 0, y = 0;

int getX() { return x; } int getY() { return y; }

void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } }

42 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 21 Aspect-oriented programming with AspectJ

with AspectJ MoveTracking v1

class Line { aspect MoveTracking { private Point p1, p2; private boolean flag = false; public boolean testAndClear() { Point getP1() { return p1; } boolean result = flag; Point getP2() { return p2; } flag = false; return result; void setP1(Point p1) { } this.p1 = p1; } pointcut move(): void setP2(Point p2) { call(void Line.setP1(Point)) || this.p2 = p2; call(void Line.setP2(Point)); } } after(): move() { flag = true; } class Point { } private int x = 0, y = 0;

int getX() { return x; } int getY() { return y; }

void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } }

43 AspectJ Tutorial

with AspectJ MoveTracking v2

class Line { aspect MoveTracking { private Point p1, p2; private boolean flag = false; public boolean testAndClear() { Point getP1() { return p1; } boolean result = flag; Point getP2() { return p2; } flag = false; return result; void setP1(Point p1) { } this.p1 = p1; } pointcut move(): void setP2(Point p2) { call(void Line.setP1(Point)) || this.p2 = p2; call(void Line.setP2(Point)) || } call(void Point.setX(int)) || } call(void Point.setY(int));

after(): move() { class Point { flag = true; private int x = 0, y = 0; } } int getX() { return x; } int getY() { return y; }

void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } }

44 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 22 Aspect-oriented programming with AspectJ

with AspectJ MoveTracking v3

class Line { aspect MoveTracking { private Point p1, p2; private Set movees = new HashSet(); public Set getmovees() { Point getP1() { return p1; } Set result = movees; Point getP2() { return p2; } movees = new HashSet(); return result; void setP1(Point p1) { } this.p1 = p1; } pointcut move(FigureElement figElt): void setP2(Point p2) { target(figElt) && this.p2 = p2; (call(void Line.setP1(Point)) || } call(void Line.setP2(Point)) || } call(void Point.setX(int)) || call(void Point.setY(int))); class Point { private int x = 0, y = 0; after(FigureElement fe): move(fe) { movees.add(fe); int getX() { return x; } } int getY() { return y; } }

void setX(int x) { this.x = x; } • evolution is more modular void setY(int y) { this.y = y; – all changes in single aspect } }

45 AspectJ Tutorial

advice is additional action to take at join points

• before before proceeding at join point

• after returning a value to join point • after throwing a throwable to join point • after returning to join point either way

• around on arrival at join point gets explicit control over when&if program proceeds

46 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 23 Aspect-oriented programming with AspectJ

contract checking simple example of before/after/around

• pre-conditions – check whether parameter is valid • post-conditions – check whether values were set • condition enforcement – force parameters to be valid

47 AspectJ Tutorial

pre-condition using before advice aspect PointBoundsPreCondition {

before(int newX): call(void Point.setX(int)) && args(newX) { assert(newX >= MIN_X); what follows the ‘:’ is assert(newX <= MAX_X); always a pointcut – } primitive or user-defined before(int newY): call(void Point.setY(int)) && args(newY) { assert(newY >= MIN_Y); assert(newY <= MAX_Y); }

private void assert(boolean v) { if ( !v ) throw new RuntimeException(); } }

48 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 24 Aspect-oriented programming with AspectJ

post-condition using after advice

aspect PointBoundsPostCondition {

after(Point p, int newX): call(void Point.setX(int)) && target(p) && args(newX) { assert(p.getX() == newX); }

after(Point p, int newY): call(void Point.setY(int)) && target(p) && args(newY) { assert(p.getY() == newY); }

private void assert(boolean v) { if ( !v ) throw new RuntimeException(); } }

49 AspectJ Tutorial

condition enforcement using around advice

aspect PointBoundsEnforcement {

void around(Point p, int newX): call(void Point.setX(int)) && target(p) && args(newX) { proceed(p, clip(newX, MIN_X, MAX_X)); }

void around(Point p, int newY): call(void Point.setY(int)) && target(p) && args(newY) { proceed(p, clip(newY, MIN_Y, MAX_Y)); }

private int clip(int val, int min, int max) { return Math.max(min, Math.min(max, val)); } }

50 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 25 Aspect-oriented programming with AspectJ

special static method

proceed(arg1, arg2, …)

available only in around advice

means “run what would have run if this around advice had not been defined”

51 AspectJ Tutorial

other primitive pointcuts

this() within() withincode()

any join point at which currently executing object is an instance of type or class name currently executing code is contained within class name currently executing code is specified method or constructor

get(int Point.x) set(int Point.x)

field reference or assignment join points

52 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 26 Aspect-oriented programming with AspectJ

using field set pointcuts

aspect PointCoordinateTracing {

pointcut coordChanges(Point p, int newVal): (set(int Point.x) || set(int Point.y)) && target(p) && args(newVal);

before(Point p, int newVal): coordChanges(p, newVal) { System.out.println(“At ” + tjp.getSignature() + “ field is changed to ” + newVal + “.”); } }

53 AspectJ Tutorial

special value reflective* access to the join point

• thisJoinPoint. Signature getSignature() Object[] getArgs() ...

available in any advice

thisJoinPoint is abbreviated to ‘tjp’ occasionally in these slides to save slide space

* introspective subset of reflection consistent with Java

54 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 27 Aspect-oriented programming with AspectJ

other primitive pointcuts

execution(void Point.setX(int)) method/constructor execution join points (at actual called method)

initialization(Point) object initialization join points

staticinitialization(Point) class initialization join points (as the class is loaded)

55 AspectJ Tutorial

context sensitive aspects MoveTracking v4

aspect MoveTracking { List movers = new LinkedList(); List movees = new LinkedList(); // … pointcut moveCalls(Object mover, FigureElement movee): this(mover) && target(movee) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)));

after(Object mover, FigureElement movee) returning: moveCalls(mover, movee) { movers.add(mover); movees.add(movee); } }

56 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 28 Aspect-oriented programming with AspectJ

fine-grained protection

class Point implement FigureElement { private int x = 0, y = 0;

int getX() { return x; } int getY() { return y; }

void setX(int nv) { primitiveSetX(nv); } void setY(int nv) { primitiveSetY(nv); }

void primitiveSetX(int x) { this.x = x; } void primitiveSetY(int y) { this.y = y; } } aspect PrimitiveSetterEnforcement { pointcut illegalSets(): !(withincode(void Point.primitiveSetX(int)) || withincode(void Point.primitiveSetY(int))) && (sets(int Point.x) || sets(int Point.y));

before(): illegalSets() { throw new Error("Illegal primitive setter call."); } }

57 AspectJ Tutorial

fine-grained protection

class Point implement FigureElement { private int x = 0, y = 0;

int getX() { return x; } int getY() { return y; }

void setX(int nv) { primitiveSetX(nv); } void setY(int nv) { primitiveSetY(nv); }

void primitiveSetX(int x) { this.x = x; } void primitiveSetY(int y) { this.y = y; } } aspect PrimitiveSetterEnforcement { pointcut illegalSets(): !(withincode(void Point.primitiveSetX(int)) || withincode(void Point.primitiveSetY(int))) && (sets(int Point.x) || sets(int Point.y));

declare error: illegalSets(): “Illegal setter call”; }

58 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 29 Aspect-oriented programming with AspectJ

other primitive pointcuts

cflow(pointcut designator) all join points within the dynamic control flow of any join point in pointcut designator.

cflowbelow(pointcut designator) all join points within the dynamic control flow below any join point in pointcut designator.

59 AspectJ Tutorial

context sensitive aspects MoveTracking v5

aspect MoveTracking {

private Set movees = new HashSet(); public Set getMovees() { Set result = movees ; movees = new HashSet(); return result; } pointcut move(FigureElement figElt): target(figElt) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)));

pointcut topLevelMove(FigureElement figElt): move(figElt) && !cflowbelow(move(FigureElement));

after(FigureElement fe) returning: topLevelMove(fe) { movees.add(fe); } } 60 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 30 Aspect-oriented programming with AspectJ

wildcarding in pointcuts

“*” is wild card “..” is multi-part wild card target(Point) target(graphics.geom.Point) target(graphics.geom.*) any type in graphics.geom target(graphics..*) any type in any sub-package of graphics

call(void Point.setX(int)) call(public * Point.*(..)) any public method on Point call(public * *(..)) any public method on any type

call(void Point.getX()) call(void Point.getY()) call(void Point.get*()) call(void get*()) any getter

call(Point.new(int, int)) call(new(..)) any constructor

61 AspectJ Tutorial

property-based crosscutting

package com.xerox.scan; public class C2 { package com.xerox.print;… package com.xerox.copy; public class C1 { public int frotz() {public class C3 { … A.doSomething(…); … public void foo() { … public String s1() { A.doSomething(…); } A.doSomething(…); … public int bar() { … } A.doSomething(…); } … … … } } } … }

• crosscuts of methods with a common property – public/private, return a certain value, in a particular package • logging, debugging, profiling – log on entry to every public method

62 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 31 Aspect-oriented programming with AspectJ

property-based crosscutting

aspect PublicErrorLogging { neatly captures public Log log = new Log(); interface of mypackage

pointcut publicInterface(): call(public * com.xerox..*.*(..));

after() throwing (Error e): publicInterface() { log.write(e); } } consider code maintenance • another programmer adds a public method • i.e. extends public interface – this code will still work • another programmer reads this code • “what’s really going on” is explicit

63 AspectJ Tutorial

aspect state what if you want a per-object log?

aspect PublicErrorLogging pertarget(PublicErrorLogging.publicInterface()) { one instance of the aspect for each object Log log = new Log(); that ever executes at these points

pointcut publicInterface(): call(public * com.xerox..*.*(..));

after() throwing (Error e): publicInterface() { log.write(e); }

64 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 32 Aspect-oriented programming with AspectJ

looking up aspect instances

:

static Log getLog(Object obj) { return (PublicErrorLogging.aspectOf(obj)).log; } }

• static method of aspects – for default aspects takes no argument – for aspects of eachcflow takes no arguments – for aspects of eachobject takes an Object • returns aspect instance

65 AspectJ Tutorial

aspect relations

pertarget() perthis() p1 one aspect instance for each l1 object that is ever “this” at p2 the join points p3 l2

percflow() percflowbelow() one aspect instance for each join point in pointcut, is available at all joinpoints in cflow or cflowbelow

66 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 33 Aspect-oriented programming with AspectJ

inheritance & specialization

• pointcuts can have additional advice – aspect with • concrete pointcut • perhaps no advice on the pointcut – in figure editor • move() can have advice from multiple aspects – module can expose certain well-defined pointcuts

• abstract pointcuts can be specialized – aspect with • abstract pointcut • concrete advice on the abstract pointcut

67 AspectJ Tutorial

a shared pointcut

public class FigureEditor { pointcut move(FigureElement figElt): target(figElt) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); ... }

aspect MoveTracking { after(FigureElement fe) returning: FigureEditor.move(fe) { ... } ... }

68 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 34 Aspect-oriented programming with AspectJ

a reusable aspect

abstract public aspect RemoteExceptionLogging {

abstract pointcut logPoint(); abstract

after() throwing (RemoteException e): logPoint() { log.println(“Remote call failed in: ” + thisJoinPoint.toString() + “(” + e + “).”); } }

public aspect MyRMILogging extends RemoteExceptionLogging { pointcut logPoint(): call(* RegistryServer.*.*(..)) || call(private * RMIMessageBrokerImpl.*.*(..)); }

69 AspectJ Tutorial

introduction (like “open classes”) MoveTracking v6

aspect MoveTracking { private Set movees = new HashSet(); introduction adds members to target type public Set getMovees() { Set result = movees; movees = new HashSet(); return result; } private Object FigureElement.lastMovedBy; public Object FigureElement.getLastMovedBy() { return lastMovedBy; }

pointcut MoveCalls(Object mover, FigureElement movee): instanceof(mover) && public and private are ( lineMoveCalls(movee) || pointMoveCalls(movee)); pointcut lineMoveCalls(Line ln): with respect to enclosing calls(void ln.setP1(Point)) || calls(void ln.setP2(Point)); pointcut pointMoveCalls (Point pt): calls(void pt.setX(int)) || calls(void pt. setY(int)); aspect declaration after(Object mover, FigureElement movee): MoveCalls(mover, movee) { movees.add(movee); movee.lastMovedBy = mover; } }

70 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 35 Aspect-oriented programming with AspectJ

summary

join points pointcuts advice method & constructor -primitive- before calls call after executions execution around field handler gets get set sets initialization introduction dispatch exception handler this target declare executions within withincode initializations cflow cflowbelow aspects -user-defined- crosscutting type pointcut pertarget declaration perthis abstract percflow overriding percflowbelow static 71 AspectJ Tutorial

where we have been… … and where we are going

problem structure Part IV: crosscutting in the design, and how to use AspectJ to capture that

AspectJ mechanisms Part II: crosscutting in the code mechanisms AspectJ provides

72 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 36 Aspect-oriented programming with AspectJ

Part III

AspectJ IDE support

programming environment

• AJDE support for – emacs, JBuilder, Forte • also jdb style debugger (ajdb) • and window-based debugger

• navigating AspectJ code • compiling • tracking errors • debugging

• ajdoc

74 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 37 Aspect-oriented programming with AspectJ

Part IV

Using Aspects

where we have been… … and where we are going

problem structure Part IV: crosscutting in the design, and how to use AspectJ to capture that

AspectJ mechanisms Part II: crosscutting in the code mechanisms AspectJ provides

76 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 38 Aspect-oriented programming with AspectJ

goals of this chapter

• present examples of aspects in design – intuitions for identifying aspects • present implementations in AspectJ – how the language support can help • work on implementations in AspectJ – putting AspectJ into practice • raise some style issues – objects vs. aspects • when are aspects appropriate?

77 AspectJ Tutorial

example 1 plug & play tracing

• plug tracing into the system – exposes join points and uses very simple advice • an unpluggable aspect – the program’s functionality is unaffected by the aspect • uses both aspect and object

78 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 39 Aspect-oriented programming with AspectJ

tracing without AspectJ

class TraceSupport { static int TRACELEVEL = 0; TraceSupport static protected PrintStream stream = null; static protected int callDepth = -1;

static void init(PrintStream _s) {stream=_s;}

static void traceEntry(String str) { if (TRACELEVEL == 0) return; callDepth++; printEntering(str); } static void traceExit(String str) { if (TRACELEVEL == 0) return; callDepth--; printExiting(str); } class Point { } void set(int x, int y) { TraceSupport.traceEntry(“Point.set”); _x = x; _y = y; TraceSupport.traceExit(“Point.set”); } 79 } AspectJ Tutorial

a clear crosscutting structure

TraceSupport all modules of the system use the trace facility in a consistent way: entering the methods and exiting the methods

this line is about interacting with the trace facility

80 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 40 Aspect-oriented programming with AspectJ

tracing as an aspect

aspect TraceMyClasses { TraceSupport pointcut tracedMethod(): within(com.bigboxco.boxes.*) && execution(* *(..));

before(): tracedMethod() { TraceSupport.traceEntry( thisJoinPoint.getSignature()); } after(): tracedMethod() { TraceSupport.traceExit( thisJoinPoint.getSignature()); } }

81 AspectJ Tutorial

plug and debug

• plug in: ajc Point.java Line.java TraceSupport.java MyClassTracing.java

• unplug: ajc Point.java Line.java

• or…

82 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 41 Aspect-oriented programming with AspectJ

plug and debug

//From ContextManager

public void service( Request rrequest , Response rresponse ) { // log( "New request " + rrequest ); try { // System.out.print("A"); // log( "New request " + rrequest ); rrequest .setContextManager( this ); rrequest .setResponse (rresponse); rresponse. setRequest (rrequest );

// wront request - parsing error // System.out.print(“A”); int status=rresponse .getStatus();

if( status < 400 ) status= processRequest( rrequest );

if(status==0) status=authenticate( rrequest, rresponse ); if(status == 0) status=authorize( rrequest, rresponse ); if( status == 0 ) { rrequest .getWrapper().handleRequest (rrequest , rresponse); } else { // something went wrong handleError ( rrequest, rresponse , null, status ); } } catch ( Throwable t) { handleError( rrequest, rresponse , t, 0 ); // System.out.print("B"); } // System.out.print("B"); try { rresponse.finish(); rrequest .recycle(); if(debug>0) rresponse.recycle(); } catch ( Throwable ex ) { if(debug>0) log( "Error closing request " + ex); log("Error closing request " + ex); } // log( "Done with request " + rrequest ); // System.out.print("C"); return; } // log("Done with request " + rrequest);

// System.out.print("C");

83 AspectJ Tutorial

plug and debug

• turn debugging on/off without editing classes • debugging disabled with no runtime cost • can save debugging code between uses • can be used for profiling, logging • easy to be sure it is off

84 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 42 Aspect-oriented programming with AspectJ

aspects in the design have these benefits

• objects are no longer responsible for using the trace facility – trace aspect encapsulates that responsibility, for appropriate objects

• if the Trace interface changes, that change is shielded from the objects – only the trace aspect is affected

• removing tracing from the design is trivial – just remove the trace aspect

85 AspectJ Tutorial

aspects in the code have these benefits

• object code contains no calls to trace functions – trace aspect code encapsulates those calls, for appropriate objects

• if the trace interface changes, there is no need to modify the object classes – only the trace aspect class needs to be modified

• removing tracing from the application is trivial – compile without the trace aspect class

86 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 43 Aspect-oriented programming with AspectJ

tracing: object vs. aspect

• using an object • using an aspect captures tracing captures the support, but does consistent usage of not capture its the tracing support consistent usage by by the objects other objects

TraceSupport TraceSupport

87 AspectJ Tutorial

tracing exercises

• Make the tracing aspect a library aspect by using an abstract pointcut. • The after advice used runs whether the points returned normally or threw exceptions, but the exception thrown is not traced. Add advice to do so.

88 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 44 Aspect-oriented programming with AspectJ

refactor TraceMyClasses into a reusable exercise (library) aspect and an extension equivalent to TraceMyClasses aspect TracingXXX { // what goes here?

}

aspect TraceMyClasses extends TracingXXX { // what goes here?

}

89 AspectJ Tutorial

we now have the Trace class, and two exercise aspects, from a design perspective, what does each implement? abstract aspect TracingProtocol {

abstract pointcut tracetracedMethod();

before(): tracedMethod() { TraceSupport.traceEntry(thisJoinPoint.getSignature()); } after(): tracedMethod() { TraceSupport.traceExit(thisJoinPoint.getSignature()); } }

aspect TraceMyClasses extends TracingProtocol {

pointcut tracedMethod(): within(com.bigboxco.boxes.*) && execution(* *(..));

}

90 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 45 Aspect-oriented programming with AspectJ

example 2 roles/views

CloneablePoint <>

clone()

HashablePoint Point ComparablePoint <> <> x: double equals(o:Object) y: double compareTo(o:Object) hashCode() theta: double rho: double rotate(angle:double) offset(dx:double,dy:double) ...

91 AspectJ Tutorial

CloneablePoint

aspect CloneablePoint {

declare parents: Point implements Cloneable;

public Object Point.clone() throws CloneNotSupportedException { // we choose to bring all fields up to date before cloning makeRectangular(); // defined in class Point makePolar(); // defined in class Point return super.clone(); } }

92 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 46 Aspect-oriented programming with AspectJ

roles/views exercise/discussion

• Write the HashablePoint and ComparablePoint aspects. • Consider a more complex system. Would you want the HashablePoint aspect associated with the Point class, or with other HashableX objects, or both?

93 AspectJ Tutorial

example 3 counting bytes

interface OutputStream { public void write(byte b); public void write(byte[] b); }

/** * This SIMPLE aspect keeps a global count of all * all the bytes ever written to an OutputStream. */ aspect ByteCounting {

int count = 0; int getCount() { return count; }

// // // what goes here? // // // } 94 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 47 Aspect-oriented programming with AspectJ

exercise complete the code for ByteCounting

/** * This SIMPLE aspect keeps a global count of all * all the bytes ever written to an OutputStream. */ aspect ByteCounting {

int count = 0; int getCount() { return count; }

}

95 AspectJ Tutorial

counting bytes v1 a first attempt

aspect ByteCounting {

int count = 0; int getCount() { return count; }

after() returning: call(void OutputStream.write(byte)) { count = count + 1; }

after(byte[] bytes) returning: call(void OutputStream.write(bytes)) { count = count + bytes.length; } }

96 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 48 Aspect-oriented programming with AspectJ

counting bytes some stream implementations

class SimpleOutputStream implements OutputStream { public void write(byte b) { … }

public void write(byte[] b) { for (int i = 0; i < b.length; i++) write(b[i]); } }

class OneOutputStream implements OutputStream { public void write(byte b) { … }

public void write(byte[] b) { … } }

97 AspectJ Tutorial

counting bytes another implementation

class OtherOutputStream implements OutputStream { public void write(byte b) { byte[] bs = new byte[1] { b }; write(bs); }

public void write(byte[] b) { … } }

98 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 49 Aspect-oriented programming with AspectJ

counting bytes v2 using cflow for more robust counting

aspect ByteCounting {

int count = 0; int getCount() { return count; }

pointcut write(): call(void OutputStream.write(byte)) || call(void OutputStream.write(byte[]));

pointcut withinWrite(): cflowbelow(write());

after() returning: !withinWrite() && call(void OutputStream .write(byte)) { count++; }

after(byte[] bytes) returning: !withinWrite() && call(void OutputStream .write(bytes)) { count = count + bytes.length; } }

99 AspectJ Tutorial

counting bytes v3 per-stream counting

aspect ByteCounting of eachobject(write()) { int count;

int getCountOf(OutputStream str) { return ByteCounting.aspectOf(str).count; }

... count++;

... count += bytes.length;

}

100 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 50 Aspect-oriented programming with AspectJ

counting bytes exercises

• How do the aspects change if the method void write(Collection c) is added to the OutputStream interface? • How would you change v2 to handle byte generators:

interface ByteGenerator { int getLength(); void generateTo(OutputStream s); }

101 AspectJ Tutorial

example 4 context-passing aspects

caller1 Service

caller2

workers need to know the caller: • capabilities • charge backs • to customize result

worker 1 worker 2 worker 3

102 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 51 Aspect-oriented programming with AspectJ

context-passing aspects

caller1 Service

caller2

workers need to know the caller: • capabilities • charge backs • to customize result

worker 1 worker 2 worker 3

103 AspectJ Tutorial

context-passing aspects

pointcut invocations(Caller c): target(c) && call(void Service.doService(String));

104 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 52 Aspect-oriented programming with AspectJ

context-passing aspects

pointcut invocations(Caller c): target(c) && call(void Service.doService(String));

pointcut workPoints(Worker w): target(w) && call(void Worker.doTask(Task));

105 AspectJ Tutorial

context-passing aspects

pointcut invocations(Caller c): target(c) && call(void Service.doService(String));

pointcut workPoints(Worker w): target(w) && call(void Worker.doTask(Task));

pointcut perCallerWork(Caller c, Worker w): cflow(invocations(c)) && workPoints(w);

106 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 53 Aspect-oriented programming with AspectJ

context-passing aspects

abstract aspect CapabilityChecking {

pointcut invocations(Caller c): target(c) && call(void Service.doService(String));

pointcut workPoints(Worker w): target(w) && call(void Worker.doTask(Task));

pointcut perCallerWork(Caller c, Worker w): cflow(invocations(c)) && workPoints(w);

before (Caller c, Worker w): perCallerWork(c, w) { w.checkCapabilities(c); } }

107 AspectJ Tutorial

example 5 properties of interfaces

interface Forest { int howManyTrees(); int howManyBirds(); ... }

pointcut forestCall(): call(* Forest.*(..));

before(): forestCall(): { }

108 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 54 Aspect-oriented programming with AspectJ

aspects on interfaces a first attempt

aspect Forestry { pointcut forestCall(): call(* Forest.*(..));

before(): forestCall() { System.out.println(tjp.getSignature() + " is a Forest-Method."); } }

109 AspectJ Tutorial

aspects on interfaces an implementation

class ForestImpl implements Forest { public static void main(String[] args) { Forest f1 = new ForestImpl();

f1.toString(); f1.howManyTrees(); f1.howManyTrees(); } public int howManyTrees() { return 100; } public int howManyBirds() { return 200; } }

• interface Forest includes methods from Object, such as toString()

110 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 55 Aspect-oriented programming with AspectJ

aspects on interfaces adding constraints

aspect Forestry { pointcut forestCall(): call(* Forest.*(..)) && !call(* Object.*(..));

before(): forestCall() { System.out.println(thisJoinPoint.methodName + " is a Forest-method."); } }

111 AspectJ Tutorial

aspects on interfaces exercises

• In this example you needed to constrain a pointcut because of undesired inheritance. Think of an example where you would want to capture methods in a super-interface. • Constraining a pointcut in this way can be seen as an aspect idiom. What other idioms have you seen in this tutorial?

112 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 56 Aspect-oriented programming with AspectJ

example 6 RMI exception aspects

client reactions to failures: - abort

113 AspectJ Tutorial - try another server

a TimeServer design

114 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 57 Aspect-oriented programming with AspectJ

the TimeService

public interface TimeService extends Remote {

/** * What's the time? */ public Date getTime() throws RemoteException;

/** * Get the name of the server */ public String getName() throws RemoteException;

/** * Exported base name for the service */ public static final String nameBase = "TimeService"; }

115 AspectJ Tutorial

the TimeServer

public class TimeServer extends UnicastRemoteObject implements TimeService { /** * The remotely accessible methods */ public Date getTime() throws RemoteException {return new Date();} public String getName() throws RemoteException {return toString();} /** * Make a new server object and register it */ no exception public static void main(String[] args) { TimeServer ts = new TimeServer(); catching here, Naming.bind(TimeService.nameBase, ts); } but notice /** * Exception pointcuts. Code is not complete without advice on them. */ pointcut create(): within(TimeServer) && call(TimeServer.new());

pointcut bind(): within(TimeServer) && call(void Naming.bind(String,..)); pointcut bindName(String name): args(name, ..) && bind(); }

116 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 58 Aspect-oriented programming with AspectJ

AbortMyServer

aspect AbortMyServer { TimeServer around(): TimeServer.create() { TimeServer result = null; try { result = proceed(); } catch (RemoteException e){ System.out.println("TimeServer err: " + e.getMessage()); System.exit(2); } return result; } declare soft: RemoteException: TimeServer.create();

void around(String name): TimeServer.bindName(name) { try { proceed(name); System.out.println("TimeServer: bound name."); } catch (Exception e) { System.err.println("TimeServer: error " + e); System.exit(1); } } declare soft: Exception: TimeServer.bind(); }

117 AspectJ Tutorial

RetryMyServer

aspect RetryMyServer { TimeServer around(): TimeServer.create() { TimeServer result = null; try { result = proceed(); } catch (RemoteException e){ System.out.println("TimeServer error."); e.printStackTrace(); } return result; } declare soft: RemoteException: TimeServer.create();

void around(String name): TimeServer.bindName(name) { for (int tries = 0; tries < 3; tries++) { try { proceed(name + tries); System.out.println("TimeServer: Name bound in registry."); return; } catch (AlreadyBoundException e) { System.err.println("TimeServer: name already bound"); } System.err.println("TimeServer: Giving up."); System.exit(1); } declare soft: Exception: TimeServer.bind(); }

118 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 59 Aspect-oriented programming with AspectJ

the Client

public class Client { again, no TimeService server = null; /** exception * Get a server and ask it the time occasionally */ catching here void run() { server = (TimeService)Naming.lookup(TimeService.nameBase); System.out.println("\nRemote Server=" + server.getName() + "\n\n"); while (true) { System.out.println("Time: " + server.getTime()); pause(); } } /** * Exception pointcuts. Code is not complete without advice on them. */ pointcut setup(): call(Remote Naming.lookup(..)); pointcut setupClient(Client c): this(c) && setup();

pointcut serve(): call(* TimeService.*(..)); pointcut serveClient(Client c, TimeService ts): this(c) && target(ts) && serve();

… other methods … } 119 AspectJ Tutorial

AbortMyClient

aspect AbortMyClient { Remote around(Client c): Client.setupClient(c) { Remote result = null; try { result = proceed(c); } catch (Exception e) { System.out.println("Client: No server. Aborting."); System.exit(0); } return result; } declare soft: Exception: Client.setup();

Object around(Client c, TimeService ts): Client.serveClient(c, ts) { Object result = null; try { result = proceed(c, ts); } catch (RemoteException e) { System.out.println("Client: Remote Exception. Aborting."); System.exit(0); } return result; } declare soft: RemoteException: Client.serve(); } 120 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 60 Aspect-oriented programming with AspectJ

RetryMyClient

aspect RetryMyClient {

Remote around(Client c): Client.setupClient(c) { Remote result = null; try { result = proceed(c);} catch (NotBoundException e) { System.out.println("Client: Trying alternative name..."); result = findNewServer(TimeService.nameBase, c.server, 3); if (result == null) System.exit(1); /* No server found */ } catch (Exception e2) { System.exit(2); } return result; } declare soft: Exception: Client.setup();

Object around(Client c, TimeService ts): Client.serveClient(c,ts) { try { return proceed(c,ts); } catch (RemoteException e) { /* Ignore and try other servers */ } c.server = findNewServer(TimeService.nameBase, c.server, 3); if (c.server == null) System.exit(1); /* No server found */ try { return thisJoinPoint.runNext(c, c.server); } catch (RemoteException e2) { System.exit(2); } return null; } declare soft: RemoteException: Client.serve();

static TimeService findNewServer(String baseName, Object currentServer, int nservers) { … } }

121 AspectJ Tutorial

building the client

• abort mode: ajc Client.java TimeServer_Stub.java AbortMyClient.java

• retry mode:

ajc Client.java TimeServer_Stub.java RetryMyClient.java

• switch to different failure handling modes without editing • no need for subclassing or delegation • reusable failure handlers

122 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 61 Aspect-oriented programming with AspectJ

RMI exception handling exercises

• Write another exception handler that, on exceptions, gives up the remote mode and instantiates a local TimeServer. • How would this client look like if the exception handling were not designed with aspects? Can you come up with a flexible OO design for easily switching between exception handlers? • Compare the design of exception handlers with aspects vs. with your OO design

123 AspectJ Tutorial

example 7 layers of functionality

• given a basic telecom operation, with customers, calls, connections • model/design/implement utilities such as – timing – consistency checks – ...

124 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 62 Aspect-oriented programming with AspectJ

telecom basic design

Customer Call Call startCall(Customer) 1 Customer getCaller() void pickupCall(Call) void pickup() void mergeCalls(Call, Call) void merge(Call) 0..N void hangupCall(Call) void hangup() 1 1 These classes 0..N define the protocols for Connection setting up calls Customer getCaller() caller Customer getReceiver() (includes void complete() receiver conference void drop() calling) and establishing connections Local LongDistance 125 AspectJ Tutorial

timing entities

store total 0..N connection Customer Call 1 time 1 1

0..N caller Connection time each connection receiver

Local LongDistance

126 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 63 Aspect-oriented programming with AspectJ

timing some actions 0..N connection dropped: Customer Call add time 1 1 1

0..N connection made: caller start timing Connection receiver connection dropped: stop timing

Local LongDistance

127 AspectJ Tutorial

timing additional design elements 0..N long getTime() Customer Call 1 1 1

0..N Timer invoke when connection drops caller void stop() void start() set and invoke Connection receiver long getTime() upon new connection

Local LongDistance

128 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 64 Aspect-oriented programming with AspectJ

timing exercise

• Write an aspect representing the timing protocol.

129 AspectJ Tutorial

timing what is the nature of the crosscutting?

• connections and calls are involved • well defined protocols among them • pieces of the timing protocol must be triggered by the execution of certain basic operations. e.g. – when connection is completed, set and start a timer – when connection drops, stop the timer and add time to customers’ connection time

130 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 65 Aspect-oriented programming with AspectJ

timing an aspect implementation

aspect Timing { private Timer Connection.timer = new Timer();

private long Customer.totalConnectTime = 0; public static long getTotalConnectTime(Customer c) { return c.totalConnectTime; }

pointcut startTiming(Connection c): target(c) && call(void c.complete()); pointcut endTiming(Connection c): target(c) && call(void c.drop());

after(Connection c): startTiming(c) { c.timer.start(); }

after(Connection c): endTiming(c) { Timer timer = c.timer; timer.stop(); long currTime = timer.getTime(); c.getCaller().totalConnectTime += currTime; c.getReceiver().totalConnectTime += currTime; } }

131 AspectJ Tutorial

timing as an object

Customer 0..N ... long getTime() 1 void addToTime(long)

addToTime(timer.getTime()) 0..N Timer Connection void stop() ... void start() drop() long getTime() new(..)

timing as an object captures timing support, but does not capture the protocols involved in implementing the timing feature

132 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 66 Aspect-oriented programming with AspectJ

timing as an aspect

Timing Customer 0..N long getTime() ... 1 void addToTime(long t) addToTime(timer.getTime())

0..N Timer Connection void stop() ... void start() drop() long getTime() new(..)

timing as an aspect captures the protocols involved in implementing the timing feature

133 AspectJ Tutorial

timing as an aspect has these benefits

• basic objects are not responsible for using the timing facility – timing aspect encapsulates that responsibility, for appropriate objects

• if requirements for timing facility change, that change is shielded from the objects – only the timing aspect is affected

• removing timing from the design is trivial – just remove the timing aspect

134 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 67 Aspect-oriented programming with AspectJ

timing with AspectJ has these benefits

• object code contains no calls to timing functions – timing aspect code encapsulates those calls, for appropriate objects

• if requirements for timing facility change, there is no need to modify the object classes – only the timing aspect class and auxiliary classes needs to be modified

• removing timing from the application is trivial – compile without the timing aspect class

135 AspectJ Tutorial

timing exercises

• How would you change your program if the interface to Timer objects changed to Timer void start() long stopAndGetTime() • What changes would be necessary without the aspect abstraction?

136 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 68 Aspect-oriented programming with AspectJ

telecom, continued layers of functionality: consistency

• ensure that all calls and connections are being shut down in the simulation

137 AspectJ Tutorial

consistency checking

store each new call Call remove calls that Customer 1 Customer getCaller() Call startCall(Customer) • hangup void pickup() void pickupCall(Call) • merge void mergeCalls(Call, Call) void merge(Call) 0..N void hangupCall(Call) void hangup() List of 1 1 0..N Calls 0..N Connection Customer getCaller() store each new connection caller Customer getReceiver() remove connections that drop void complete() receiver void drop() List of 0..N Connections

Local LongDistance check at the end 138 of simulation AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 69 Aspect-oriented programming with AspectJ

consistency checking

aspect ConsistencyChecker { Vector calls = new Vector(), connections = new Vector(); /* The lifecycle of calls */ after(Call c): target(c) && call(Call.new(..)) { calls.addElement(c); } after(Call c): target(c) && call(* Call.hangup(..)) { calls.removeElement(c); } after(Call other): args(other) && (void Call.merge(Call)) { calls.removeElement(other); }

/* The lifecycle of connections */ after(Connection c): target(c) && call(Connection.new(..)) { connections.addElement(c); } after(Connection c): target(c) && call(* Connection.drop(..)) { connections.removeElement(c); } after(): within(TelecomDemo) && executions(void main(..)) { if (calls.size() != 0) println("ERROR on calls clean up."); if (connections.size()!=0) println("ERROR on connections clean up."); } }

139 AspectJ Tutorial

summary so far

• presented examples of aspects in design – intuitions for identifying aspects • presented implementations in AspectJ – how the language support can help • raised some style issues – objects vs. aspects

140 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 70 Aspect-oriented programming with AspectJ

when are aspects appropriate?

• is there a concern that: – crosscuts the structure of several objects or operations – is beneficial to separate out

141 AspectJ Tutorial

… crosscutting

• a design concern that involves several objects or operations • implemented without AOP would lead to distant places in the code that – do the same thing • e.g. traceEntry(“Point.set”) • try grep to find these [Griswold] – do a coordinated single thing • e.g. timing, observer pattern • harder to find these

142 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 71 Aspect-oriented programming with AspectJ

… beneficial to separate out

• does it improve the code in real ways? – separation of concerns • e.g . think about service without timing – clarifies interactions, reduces tangling • e.g. all the traceEntry are really the same – easier to modify / extend • e.g. change the implementation of tracing • e.g. abstract aspect re-use – plug and play • tracing aspects unplugged but not deleted

143 AspectJ Tutorial

good designs summary

• capture “the story” well • may lead to good implementations, measured by – code size – tangling learned through – coupling experience, influenced – etc. by taste and style

144 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 72 Aspect-oriented programming with AspectJ

expected benefits of using AOP

• good modularity, even in the presence of crosscutting concerns – less tangled code, more natural code, smaller code – easier maintenance and evolution • easier to reason about, debug, change – more reusable • more possibilities for plug and play • abstract aspects

145 AspectJ Tutorial

Part V

References, Related Work

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 73 Aspect-oriented programming with AspectJ

AOP and AspectJ on the web

• aspectj.org • www.parc.xerox.com/aop

147 AspectJ Tutorial

Workshops

• ECOOP’97 – http://wwwtrese.cs.utwente.nl/aop-ecoop97 • ICSE’98 – http://www.parc.xerox.com/aop/icse98 • ECOOP’98 – http://wwwtrese.cs.utwente.nl/aop-ecoop98 • ECOOP’99 – http://wwwtrese.cs.utwente.nl/aop-ecoop99 • OOPSLA’99 – http://www.cs.ubc.ca/~murphy/multid-workshop-oopsla99/index.htm • ECOOP’00 – http://trese.cs.utwente.nl/Workshops/adc2000/ • OOPSLA’00 – http://trese.cs.utwente.nl/Workshops/OOPSLA2000/ • ECOOP’01

148 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 74 Aspect-oriented programming with AspectJ

growing interest in separation of crosscutting concerns • aspect-oriented programming – @ U Twente • [Aksit] – adaptive programming @ Northeastern U • [Lieberherr] • multi-dimensional separation of concerns @ IBM – [Ossher, Tarr] • assessment of SE techniques @ UBC – [Murphy] • information transparency @ UCSD – [Griswold] • …

149 AspectJ Tutorial

AOP future – idea, language, tools

• objects are • aspects are • code and state • • “little computers” • • message as goal • • hierarchical structure • + crosscutting structure • languages support • languages support • encapsulation • • polymorphism • • inheritance • + crosscutting • tools • tools • browser, editor, debuggers • preserve object abstraction • • + preserve aspect abstraction

150 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 75 Aspect-oriented programming with AspectJ

AOP future

• language design – more dynamic crosscuts, type system … • tools – more IDE support, aspect discovery, re-factoring, re- cutting… • software engineering – finding aspects, modularity principles, … • metrics – measurable benefits, areas for improvement • theory – type system for crosscutting, fast compilation, advanced crosscut constructs

151 AspectJ Tutorial

AspectJ & the Java platform

• AspectJ is a small extension to the Java programming language – all valid programs written in the Java programming language are also valid programs in the AspectJ programming language • AspectJ has its own compiler, ajc – ajc runs on Java 2 platform – ajc is available under Open Source license – ajc produces Java platform compatible .class files

152 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 76 Aspect-oriented programming with AspectJ

AspectJ status

• release status – 3 major, ~18 minor releases over last year (1.0alpha is current) – tools • IDE extensions: Emacs , JBuilder 3.5, JBuilder 4, Forte4J • ajdoc to parallel javadoc • debugger: command line, GUI, & IDE – license • compiler, runtime and tools are free for any use • compiler and tools are Open Source • aspectj.org – May 1999: 90 downloads/mo, 20 members on users list – Feb 2001: 600 downloads/mo, 600 members on users list

• tutorials & training – 3 tutorials in 1999, 8 in 1999, 12 in 2000

153 AspectJ Tutorial

AspectJ future continue building language, compiler & tools • 1.0 – minor language tuning – incremental compilation, compilation to bytecodes • 1.1 – faster incremental compiler (up to 5k classes) – source of target classes not required – at least one more IDE • 2.0 – new dynamic crosscut constructs

commercialization decision after 1.0

154 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 77 Aspect-oriented programming with AspectJ

credits

AspectJ.org is a Xerox PARC project: Bill Griswold, Erik Hilsdale, Jim Hugunin, Vladimir Ivanovic, Mik Kersten, Gregor Kiczales, Jeffrey Palm

slides, compiler, tools & documentation are available at aspectj.org

partially funded by DARPA under contract F30602-97-C0246

155 AspectJ Tutorial

(c) Copyright Xerox Corporation, 1998- 2001. All rights reserved. 78