AspectOriented Programming with Aspect J Julie Waterhouse and

  • Slides: 116
Download presentation
Aspect-Oriented Programming with Aspect. J™ Julie Waterhouse and Mik Kersten eclipse. org/aspectj IBM UBC

Aspect-Oriented Programming with Aspect. J™ Julie Waterhouse and Mik Kersten eclipse. org/aspectj IBM UBC CASCON '04

outline • I AOP and Aspect. J overview – problems, basic concepts, context •

outline • I AOP and Aspect. J overview – problems, basic concepts, context • II Aspect. J tutorial – first example – language mechanisms – using aspects • III examples and demo • IV conclusion 2 CASCON '04

good modularity • XML parsing in org. apache. tomcat – red shows relevant lines

good modularity • XML parsing in org. apache. tomcat – red shows relevant lines of code – nicely fits in one box 3 CASCON '04 XML parsing

good modularity URL pattern matching • URL pattern matching in org. apache. tomcat –

good modularity URL pattern matching • URL pattern matching in org. apache. tomcat – red shows relevant lines of code – nicely fits in two boxes (using inheritance) 4 CASCON '04

problems like… logging is not modularized • where is logging in org. apache. tomcat

problems like… logging is not modularized • where is logging in org. apache. tomcat 5 – red shows lines of code that handle logging – not in just one place – not even in a small number of places CASCON '04

problems like… session expiration is not modularized Standard. Session Application. Session /* * ==================================

problems like… session expiration is not modularized Standard. Session Application. Session /* * ================================== * * The Apache Software License, Version 1. 1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement : * "This product includes software developed by the * Apache Software Foundation (http: //www. apache. org/). " * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache. org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http: //www. apache. org/>. * * [Additional notices, if required by prior licensing conditions] * */ public void invalidate() { server. Session. remove. Application. Session(context package org. apache. tomcat. session; // remove everything in the session Enumeration enum = values. keys(); while ( enum. has. More. Elements ()) { String name = ( String)enum. next. Element (); remove. Value(name ); } valid = false; } public boolean is. New () { if (! valid) { String msg = sm. get. String("application. Session. session. ise throw new "); import import import import if ( this. Access. Time == creation. Time ) { return true; } else { return false; } /** * @deprecated */ public void put. Value(String name, Object value) { set. Attribute(name , value); } public void set. Attribute(String name, Object value) { if (! valid) { String msg = sm. get. String("application. Session. session. ise "); Illegal. State. Exception(msg ); if (name == null) { String msg = sm. get. String("application. Session. value. iae throw new final class Standard. Session implements Http. Session , Session { (( Http. Session. Binding. Listener)value). value. Bound(e ); } "); Illegal. State. Exception(msg ); } if (name == null) { String msg = sm. get. String("application. Session. value. iae "); Illegal. Argument. Exception(msg ); implements Http. Session { return values. get(name); private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session "); private Hashtable values = new Hashtable (); private String id; private Server. Session server. Session ; private Context context; private long creation. Time = System. current. Time. Millis (); ; private long this. Access. Time = creation. Time ; private long last. Accessed = creation. Time ; private int inactive. Interval = -1; private boolean valid = true; /** * The collection of user data attributes associated with this Session. */ private Hashtable attributes = new Hashtable (); /** * The session identifier of this Session. */ private String id = null; /** * Descriptive information describing this Session implementation. */ private static final String info = "Standard. Session/1. 0"; while ( e. has. More. Elements ()) { names. add. Element(e. next. Element ()); } value. Names = new String[names. size()]; != -1) { *= 60; public Enumeration get. Attribute. Names () { if (! valid) { String msg = sm. get. String("application. Session. session. ise Server. Session get. Server. Session () { return server. Session ; } throw new "); Illegal. State. Exception(msg ); } /** * The Manager with which this Session is associated. */ private Manager manager = null; /** * The maximum time interval, in seconds, between client requests before * the servlet container may invalidate this session. A negative time * indicates that the session should never time out. */ private int max. Inactive. Interval = -1; return ( Enumeration)values. Clone. keys (); } void accessed() { // set last accessed to this. Access. Time as it will be left over // from the previous access last. Accessed = this. Access. Time ; this. Access. Time = System. current. Time. Millis (); /** * @deprecated */ public void remove. Value(String remove. Attribute(name ); } validate(); name) { /** * Flag indicating whether this session is new or } public void remove. Attribute(String name) { if (! valid) { String msg = sm. get. String("application. Session. session. ise void validate() { // if we have an inactive interval, check to see if we've exceeded it if ( inactive. Interval != -1) { int this. Interval = ( int)(System. current. Time. Millis () - last. Accessed ) / 1000; throw new "); */ private boolean /** * Return the <code> is. Valid </code> flag for this session. */ boolean is. Valid () { // HTTP SESSION IMPLEMENTATION METHODS "); is. New = true; /** * The string manager for this package. */ private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. se if (o instanceof Http. Session. Binding. Listener ) { Http. Session. Binding. Event e = new Http. Session. Binding. Event(this, name ); (( Http. Session. Binding. Listener)o). value. Unbound(e ssion "); /** * The HTTP session context associated with this session. */ private static Http. Session. Context session. Context = null; } values. remove(name); } public void set. Max. Inactive. Interval(int interval) { if (! valid) { String msg = sm. get. String("application. Session. session. ise "); throw new "); /** * The current accessed time for this session. */ private long this. Accessed. Time = creation. Time ; Illegal. State. Exception(msg ); } inactive. Interval = interval; } } /** * * @deprecated */ public int get. Max. Inactive. Interval () { if (! valid) { String msg = sm. get. String("application. Session. session. ise throw new public Http. Session. Context get. Session. Context () { return new Session. Context. Impl (); } "); // ------------------------Http. Session Properties return inactive. Interval ; } } //-----------------------------------"); // Deserialize the scalar instance variables (except Manager) creation. Time = ((Long) stream. read. Object()). long. Value (); id = (String) stream. read. Object (); last. Accessed. Time = ((Long) stream. read. Object()). long. Value (); max. Inactive. Interval = ((Integer) stream. read. Object()). int. Value (); is. New = ((Boolean) stream. read. Object()). boolean. Value (); is. Valid = ((Boolean) stream. read. Object()). boolean. Value (); /** * Return the time when this session was created, in milliseconds since * midnight, January 1, 1970 GMT. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public long get. Creation. Time () { } // -------------------------- Session Properties /** * Set the creation time for this session. This method is called by the * Manager when an existing Session instance is reused. * * @ param time The new creation time */ public void set. Creation. Time(long time) { this. creation. Time = time; this. last. Accessed. Time = time; this. Accessed. Time = time; Illegal. State. Exception(msg ); } } } /** * Return the session identifier for this session. */ public String get. Id () { } // -----------------------Http. Session Public Methods } if ((this. id != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). remove(this); Server. Sess on return (attributes. keys()); } /** * Return descriptive information about this Session implementation and * the corresponding version number, in the format * <code>& lt; description> /< version&gt ; </code>. */ public String get. Info () { return (this. info); } /** * Return the object bound with the specified name in this session, or * <code>null</code> if no object is bound with that name. * * @ param name Name of the value to be returned * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> get. Attribute ()</code> */ public Object get. Value(String name) { /** * The string manager for this package. */ private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session return ( get. Attribute(name )); return ( this. last. Accessed. Time ); } Vector results = new Vector(); Enumeration attrs = get. Attribute. Names (); while ( attrs. has. More. Elements ()) { String attr = (String) attrs. next. Element (); results. add. Element(attr ); } String names[] = new String[results. size()]; for ( int i = 0; i < names. length; i++) names[i] = (String) results. element. At(i ); return (names); return (this. manager); } /** * Set the Manager within which this Session is valid. * * @ param manager The new Manager */ public void set. Manager(Manager manager) { this. manager = manager; } /** * Return the maximum time interval, in seconds, between client requests * before the servlet container will invalidate the session. A negative * time indicates that the session should never time out. * * @exception Illegal. State. Exception if this method is called on * an invalidated session */ public int get. Max. Inactive. Interval () { return ( this. max. Inactive. Interval ); } /** * Set the maximum time interval, in seconds, between client requests * before the servlet container will invalidate the session. A negative * time indicates that the session should never time out. * * @ param interval The new maximum interval */ public void set. Max. Inactive. Interval(int interval) { this. max. Inactive. Interval = interval; private Vector dummy = new Vector(); /** * Return the session identifiers of all sessions defined * within this context. * * @deprecated As of Java Servlet API 2. 1 with no replacement. * This method must return an empty <code>Enumeration</code> * and will be removed in a future version of the API. */ public Enumeration get. Ids () { } return (dummy. elements()); /** * Invalidates this session and unbinds any objects bound to it. * * @exception Illegal. State. Exception if this method is called on * an invalidated session */ public void invalidate() { /** * Return the <code> Http. Session </code> associated with the * specified session identifier. * * @ param id Session identifier for which to look up a session * * @deprecated As of Java Servlet API 2. 1 with no replacement. * This method must return null and will be removed in a * future version of the API. */ public Http. Session get. Session(String id) { // Cause this session to expire(); } return (null); /** * Return <code>true</code> if the client does not yet know about the * session, or if the client chooses not to join the session. For * example, if the server used only cookie-based sessions, and the client * has disabled the use of cookies, then a session would be new on each * request. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public boolean is. New () { return ( this. New ); } } return (null); } * should be. ())); public Http. Session create. Session(Context * return * @author Craig R. Mc. Clanahan ctx) { manager. create. Session(). get. Session (); } */ public final class Standard. Session. Manager * Remove all sessions because our associated Context is being shut down. implements Session. Manager { * ())); * @ param ctx The context that is being shut down */ // -----------------------------Constructors public void remove. Sessions(Context ctx) { // contexts, we just want to remove the sessions of Session. Manager that adapts to the corresponding if (manager */ ((Lifecycle) manager). stop(); manager = new if (manager } catch ( Lifecycle. Exception Standard. Manager (); throw new instanceof Lifecycle) { } ((Lifecycle) manager). configure(null); ((Lifecycle) manager). start(); } catch ( Lifecycle. Exception throw new } e) { Illegal. State. Exception ("" + e); /** } * Used by context to configure the session manager's inactivity timeout. * * The Session. Manager may have some default session time out, the * Context on the other hand has it's timeout set by the deployment * descriptor ( web. xml ). This method lets the Context // Validate and update our current component state if (!started) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. not. Started ")); started = false; // Stop the background reaper thread. Stop (); Server. Session. Manager // Expire all active sessions Session sessions[] = find. Sessions (); for ( int i = 0; i < sessions. length; i++) { Standard. Session session = ( Standard. Session ) sessions[i]; if (! session. is. Valid ()) continue; session. expire(); } } = check. Interval ; } // ---------------------------- Private Methods /** * Return descriptive information about this Manager implementation and * the corresponding version number, in the format * <code>& lt; description> /< version&gt ; </code>. */ public String get. Info () { /** * Invalidate all sessions that have expired. */ private void process. Expires () { long time. Now = System. current. Time. Millis (); Session sessions[] = find. Sessions (); return (this. info); // XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later. package org. apache. tomcat. session; import org. apache. tomcat. util. *; import org. apache. tomcat. core. *; import java. io. *; import java. net. *; import java. util. *; import javax. servlet. http. *; synchronized void reap() { Enumeration enum = sessions. keys(); while ( enum. has. More. Elements ()) { Object key = enum. next. Element (); Server. Session session = ( Server. Session)sessions. get(key ); /** * * @author James Duncan Davidson [ duncan@eng. sun. com ] * @author Jason Hunter [ jch@eng. sun. com ] * @author James Todd [gonzo@eng. sun. com] */ public class Server. Session. Manager for ( int i = 0; i < sessions. length; i++) { Standard. Session session = ( Standard. Session ) sessions[i]; if (! session. is. Valid ()) continue; int max. Inactive. Interval = session. get. Max. Inactive. Interval (); if ( max. Inactive. Interval < 0) continue; int time. Idle = // Truncate, do not round up ( int) (( time. Now - session. get. Last. Accessed. Time ()) / 1000 L); if ( time. Idle >= max. Inactive. Interval ) session. expire(); } } /** * Return the maximum number of active Sessions allowed, or -1 for * no limit. */ public int get. Max. Active. Sessions () { return ( this. max. Active. Sessions ); } session. reap(); session. validate(); } } } implements Session. Manager { synchronized void remove. Session(Server. Session String id = session. get. Id (); private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session "); private static Server. Session. Manager manager; // = new Server. Session. Manager (); session. invalidate(); sessions. remove(id); session) { /** * Set the maximum number of actives Sessions allowed, or -1 for * no limit. * * @ param max The new maximum number of sessions */ public void set. Max. Active. Sessions(int max) { /** * Sleep for the duration specified by the <code> * property. */ private void thread. Sleep () { static { manager = new } this. max. Active. Sessions = -1; public void Enumera remove. Sessions(Context } // ----------------------------- Public Methods } /** * Construct and return a new session object, based on the default * settings specified by this Manager's properties. The session * id will be assigned by this method, and available via the get. Id () * method of the returned session. If a new session cannot be created * for any reason, return <code>null</code>. * * @exception Illegal. State. Exception if a new session cannot be * instantiated for any reason */ public Session create. Session () { /** * Start the background thread that will periodically check for * session timeouts. */ private void thread. Start () { get. Manager () { private Server. Session. Manager () { reaper = Reaper. get. Reaper (); reaper. set. Server. Session. Manager(this ); reaper. start(); } public void accessed( Context ctx, Request req, String id ) { Application. Session ap. S=(Application. Session)find. Session ( ctx, id); if( ap. S==null) return; if (( max. Active. Sessions >= 0) && (sessions. size() >= max. Active. Sessions )) throw new Illegal. State. Exception ( sm. get. String("standard. Manager. create. Session. ise Server. Session serv. S =ap. S. get. Server. Session (); serv. S. accessed (); ap. S. accessed (); } = max; context) { Server. Session. Manager (); private Hashtable sessions = new Hashtable (); private Reaper reaper; } check. Interval </code> try { Thread. sleep(check. Interval * 1000 L); } catch ( Interrupted. Exception e) { ; } } protected int inactive. Interval if (thread != null) return; thread. Done = false; thread = new Thread(this, thread. set. Daemon(true ); thread. start(); /** * Stop the background thread that is periodically checking for * session timeouts. */ private void thread. Stop () { } // cache it - no need to compute it again req. set. Session ( ap. S ); } if (thread == null) return; public Http. Session create. Session(Context ctx) { String session. Id = Session. Id. Generator. generate. Id (); Server. Session session = new Server. Session(session. Id ); sessions. put(session. Id , session); if(-1 != inactive. Interval ) { session. set. Max. Inactive. Interval(inactive. Interval thread. Name ); } ")); return ( super. create. Session ()); thread. Done = true; thread. interrupt(); try { thread. join(); } catch ( Interrupted. Exception ; } ); } return session. get. Application. Session ( ctx, true ); } e) { thread = null; public Http. Session find. Session(Context ctx, String id) { Server. Session s. Session =(Server. Session)sessions. get(id ); if(s. Session ==null) return null; } return s. Session. get. Application. Session(ctx , false); // --------------------------- Background Thread } /** * The background thread that checks for session timeouts and shutdown. */ public void run() { // Loop until the termination semaphore is set while (! thread. Done ) { thread. Sleep (); process. Expires (); } } e) { Illegal. State. Exception ("" + e); } try { } } } this. check. Interval instanceof Lifecycle) { try { public Standard. Session. Manager () { // --------------------------- Instance Variables /** * Set the check interval (in seconds) for this Manager. * * @ param check. Interval The new check interval */ public void set. Check. Interval(int check. Interval ) { ctx! // The manager will still run after that ( i. e. keep database // connection open * implementation. conforgure the * session manager according to this value. * * @ param minutes The session inactivity timeout in minutes. */ /** public void * The Manager implementation we are actually using. set. Session. Time. Out(int minutes) { if(-1 != minutes) { */ // The manager works with seconds. . . private Manager manager = null; manager. set. Max. Inactive. Interval(minutes } } { manager. find. Session(id ); session. get. Session (); } catch ( IOException e) { Session. Manager interface) * a Context to tell the Manager that we create what the default session * timeout for this web application (specified in the deployment descriptor) // Start the background reaper thread. Start (); } CASCON 04 return * XXX - At present, there is no way (via the for /** * Gracefully terminate the active use of the public methods of this * component. This method should be the last one called on a given * instance of this component. * * @exception Illegal. State. Exception if this component has not been started * @exception Illegal. State. Exception if this component has already * been stopped * @exception Lifecycle. Exception if this component detects a fatal error * that needs to be reported */ public void stop() throws Lifecycle. Exception { } 6 Session session = if(session!=null) * <p> ())); // Validate and update our current component state if (!configured) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. not. Configured ")); if (started) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. already. Started ")); started = true; return ( this. check. Interval ); public static Server. Session. Manager return manager; } } try { * collection class, and has minimal knowledge of the detailed request * Create a new Manager /** * Return the check interval (in seconds) for this Manager. */ public int get. Check. Interval () { // ------------------------------- Private Class /** * Return the Manager within which this Session is valid. */ public Manager get. Manager () { public Http. Session find. Session ( Context ctx, String id ) { * processing semantics of handling sessions. "); // ------------------------------- Properties s): s & ( int get. Max. Inactive. Interval () | long get. Creation. Time () | Object get. Attribute(String ) | Enumeration get. Attribute. Names () | String[] get. Value. Names () | void invalidate() | boolean is. New () | void remove. Attribute(String ) | void set. Attribute(String , Object)); implements Http. Session. Context // XXX should we throw exception or just return null ? ? } /** * This class is a dummy implementation of the <code> Http. Session. Context </code> * interface, to conform to the requirement that such an object be returned * when <code> Http. Session. get. Session. Context ()</code> is called. * * @author Craig R. Mc. Clanahan * * @deprecated As of Java Servlet API 2. 1 with no replacement. The * interface will be removed in a future version of this API. */ Http. Session - avoid another find req. set. Session ( session ); } Once we commit to the new Manager/Session The Tomcat. Next "Manager" interface acts more like a // XXX a manager may be shared by multiple /** * Name to register for the background thread. */ private String thread. Name = "Standard. Manager "; } final class Standard. Session. Context // cache the * <b>IMPLEMENTATION NOTE</b>: * paradigm, I would suggest moving the logic implemented here back into * the core level. /** * The background thread completion semaphore. */ private boolean thread. Done = false; } /** * Return the set of names of objects bound to this session. If there * are no such objects, a zero-length array is returned. * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> get. Attribute. Names ()</code> */ public String[] get. Value. Names () { ((Session) session). access(); org. apache. tomcat. core. Session. Manager * and lifecycle configuration is not supported. * Prepare for the beginning of active use of the public methods of this * component. This method should be called after <code>configure()</code>, * and before any of the public methods of the component are utilized. * * @exception Illegal. State. Exception if this component has not yet been * configured (if required for this component) * @exception Illegal. State. Exception if this component has already been * started * @exception Lifecycle. Exception if this component detects a fatal error * that prevents this component from being used */ public void start() throws Lifecycle. Exception { /** * The background thread. */ private Thread thread = null; } /** * Return the last time the client sent a request associated with this * session, as the number of milliseconds since midnight, January 1, 1970 * GMT. Actions that your application takes, such as getting or setting * a value associated with the session, do not affect the access time. */ public long get. Last. Accessed. Time () { instanceof Session) * <p> /** * Has this component been started yet? */ private boolean started = false; static advice(Standard. Session s): invalidate(s) { before { if (! s. is. Valid ()) throw new Illegal. State. Exception ( s. sm. get. String("standard. Session. " + this. Join. Point. method. Name + ". ise")); } } ctx, Request req, String id) { Http. Session session= find. Session(ctx , id); if( session == null) return; if (session /** /** * The maximum number of active Sessions allowed, or -1 for no limit. */ protected int max. Active. Sessions = -1; } crosscut invalidate(Standard. Session this. id = id; if ((manager != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). add(this); * @ param session The session to be marked public void accessed(Context } // Serialize the attribute count and the attribute values stream. write. Object(new Integer(results. size())); Enumeration names = results. elements(); while ( names. has. More. Elements ()) { String name = (String) names. next. Element (); stream. write. Object(name ); stream. write. Object(attributes. get(name )); } /** * Return an <code>Enumeration</code> of <code>String</code> objects * containing the names of the objects bound to this session. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public Enumeration get. Attribute. Names () { This should be Request. Interceptor. * */ import org. apache. tomcat. core. Session. Manager ; import org. apache. tomcat. util. Session. Util ; * <p> node = attributes. get. Named. Item("max. Inactive. Interval "); if (node != null) { try { set. Max. Inactive. Interval(Integer. parse. Int(node. get. Node. Value } catch ( Throwable t) { ; // XXX - Throw exception? } } /** * The descriptive information about this implementation. */ private static final String info = "Standard. Manager/1. 0"; // Accumulate the names of serializable attributes Vector results = new Vector(); Enumeration attrs = get. Attribute. Names (); while ( attrs. has. More. Elements ()) { String attr = (String) attrs. next. Element (); Object value = attributes. get(attr ); if (value instanceof Serializable ) results. add. Element(attr ); } return (attributes. get(name)); return (this. id); * Mark the specified session's last accessed time. * called for each request by a import org. apache. tomcat. core. Request; import org. apache. tomcat. core. Response; * XXX - At present, use of <code> Standard. Manager </code> is hard coded, node = attributes. get. Named. Item("max. Active. Sessions "); if (node != null) { try { set. Max. Active. Sessions(Integer. parse. Int(node. get. Node. Value } catch ( Throwable t) { ; // XXX - Throw exception? } } } /** * Set the session identifier for this session. * * @ param id The new session identifier */ public void set. Id(String id) { /** import org. apache. tomcat. catalina. *; import org. apache. tomcat. core. Context; * Specialized implementation of node = attributes. get. Named. Item("check. Interval "); if (node != null) { try { set. Check. Interval(Integer. parse. Int(node. get. Node. Value } catch ( Throwable t) { ; // XXX - Throw exception? } } /** * Has this component been configured yet? */ private boolean configured = false; // Write the scalar instance variables (except Manager) stream. write. Object(new Long(creation. Time )); stream. write. Object(id ); stream. write. Object(new Long(last. Accessed. Time )); stream. write. Object(new Integer(max. Inactive. Interval )); stream. write. Object(new Boolean(is. New )); stream. write. Object(new Boolean(is. Valid )); /** * Return the object bound with the specified name in this session, or * <code>null</code> if no object is bound with that name. * * @ param name Name of the attribute to be returned * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public Object get. Attribute(String name) { import javax. servlet. http. Cookie ; import javax. servlet. http. Http. Session ; * that adapts to the new component-based Manager implementation. ")); // Parse and process our configuration parameters if (!(" Manager". equals(parameters. get. Node. Name ()))) return; Named. Node. Map attributes = parameters. get. Attributes (); Node node = null; /** * The interval (in seconds) between checks for expired sessions. */ private int check. Interval = 60; /** * Write a serialized version of this session object to the specified * object output stream. * <p> * <b>IMPLEMENTATION NOTE</b>: The owning Manager will not be stored * in the serialized representation of this Session. After calling * <code> read. Object ()</code>, you must set the associated Manager * explicitly. * <p> * <b>IMPLEMENTATION NOTE</b>: Any attribute that is not Serializable * will be silently ignored. If you do not want any such attributes, * be sure the <code>distributable</code> property of our associated * Manager is set to <code>true</code>. * * @ param stream The output stream to write to * * @exception IOException if an input/output error occurs */ private void write. Object(Object. Output. Stream stream) throws IOException { if ( session. Context == null) session. Context = new Standard. Session. Context (); return ( session. Context ); // Validate and update our current component state if (configured) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. already. Configured configured = true; if (parameters == null) return; // --------------------------- Instance Variables } /** * Return the session context with which this session is associated. * * @deprecated As of Version 2. 1, this method is deprecated and has no * replacement. It will be removed in a future version of the * Java Servlet API. */ public Http. Session. Context get. Session. Context () { import java. io. IOException ; /** * Configure this component, based on the specified configuration * parameters. This method should be called immediately after the * component instance is created, and before <code>start()</code> * is called. * * @ parameters Configuration parameters for this component * (<B>FIXME: What object type should this really be? ) * * @exception Illegal. State. Exception if this component has already been * configured and/or started * @exception Lifecycle. Exception if this component detects a fatal error * in the configuration parameters it was given */ public void configure(Node parameters) throws Lifecycle. Exception { public final class Standard. Manager extends Manager. Base implements Lifecycle, Runnable { // Deserialize the attribute count and attribute values int n = ((Integer) stream. read. Object()). int. Value (); for ( int i = 0; i < n; i++) { String name = (String) stream. read. Object (); Object value = (Object) stream. read. Object (); attributes. put(name, value); } return ( this. creation. Time ); Illegal. State. Exception(msg ); } Http. Session Private Methods /** * Read a serialized version of this session object from the specified * object input stream. * <p> * <b>IMPLEMENTATION NOTE</b>: The reference to the owning Manager * is not restored by this method, and must be set explicitly. * * @ param stream The input stream to read from * * @exception Class. Not. Found. Exception if an unknown class is specified * @exception IOException if an input/output error occurs */ private void read. Object(Object. Input. Stream stream) throws Class. Not. Found. Exception , IOException { ); } Illegal. State. Exception(msg ); } } // ---------------------- java. io. IOException ; java. util. Enumeration; java. util. Hashtable ; java. util. Vector; org. apache. tomcat. catalina. *; javax. servlet. http. Cookie ; javax. servlet. http. Http. Session ; org. apache. tomcat. util. String. Manager ; org. w 3 c. dom. Named. Node. Map; org. w 3 c. dom. Node; /** * Standard implementation of the <b>Manager</b> interface that provides * no session persistence or distributable capabilities, but does support * an optional, configurable, maximum number of active sessions allowed. * <p> * Lifecycle configuration of this component assumes an XML node * in the following format: * <code> * & lt; Manager class. Name ="org. apache. tomcat. session. Standard. Manager " * check. Interval ="60" max. Active. Sessions ="-1" * max. Inactive. Interval ="-1" /> * </code> * where you can adjust the following parameters, with default values * in square brackets: * <ul> * <li><b> check. Interval </b> - The interval (in seconds) between background * thread checks for expired sessions. [60] * <li><b> max. Active. Sessions </b> - The maximum number of sessions allowed to * be active at once, or -1 for no limit. [-1] * <li><b> max. Inactive. Interval </b> - The default maximum number of seconds of * inactivity before which the servlet container is allowed to time out * a session, or -1 for no limit. This value should be overridden from * the default session timeout specified in the web application deployment * descriptor, if any. [-1] * </ ul> * * @author Craig R. Mc. Clanahan * @version $Revision: 1. 1 $ $Date: 2000/05/02 21: 28: 30 $ */ this. New = is. New ; } /** * Set the <code> is. Valid </code> flag for this session. * * @ param is. Valid The new value for the <code> is. Valid </code> flag */ void set. Valid(boolean is. Valid ) { } /** * Flag indicating whether this session is valid or not. */ private boolean is. Valid = false; Illegal. Argument. Exception(msg ); Object o = values. get(name); "); flag // ----------------------------- Public Methods package org. apache. tomcat. session; // --------------------------- Lifecycle Methods synchronized (attributes) { remove. Attribute(name ); attributes. put(name, value); if (value instanceof Http. Session. Binding. Listener ) (( Http. Session. Binding. Listener ) value). value. Bound (new Http. Session. Binding. Event((Http. Session ) this, name)); } /** * Set the <code> is. New </code> flag for this session. * * @ param is. New The new value for the <code> is. New </code> Standard. Session. Manager package org. apache. tomcat. session; import import import if ((manager != null) && manager. get. Distributable () && !(value instanceof Serializable )) throw new Illegal. Argument. Exception ( sm. get. String("standard. Session. set. Attribute. iae ")); return ( this. Valid ); } this. Valid = is. Valid ; } throw new } // ------------------------ Session Package Methods Standard. Manager Sess on n ercep or /** * Bind an object to this session, using the specified name. If an object * of the same name is already bound to this session, the object is * replaced. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Bound ()</code> on the object. * * @ param name Name to which the object is bound, cannot be null * @ param value Object to be bound, cannot be null * * @exception Illegal. Argument. Exception if an attempt is made to add a * non- serializable object in an environment marked distributable. * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public void set. Attribute(String name, Object value) { Illegal. State. Exception(msg ); if (name == null) { String msg = sm. get. String("application. Session. value. iae throw new remove. Attribute(name ); } not. } if ( this. Interval > inactive. Interval ) { invalidate(); } } } public long get. Last. Accessed. Time () { if (valid) { return last. Accessed ; } else { String msg = sm. get. String("application. Session. session. ise attributes. clear(); creation. Time = 0 L; id = null; last. Accessed. Time = 0 L; manager = null; max. Inactive. Interval = -1; is. New = true; is. Valid = false; // Tell our Manager that this Session has been recycled if ((manager != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). recycle(this); */ void set. New(boolean is. New ) { Hashtable values. Clone = ( Hashtable)values. clone (); /** * Called by context when request comes in so that accesses and * inactivities can be dealt with accordingly. */ throw new // Reset the instance variables associated with this /** * The last accessed time for this Session. */ private long last. Accessed. Time = creation. Time ; } } /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Unbound ()</code> on the object. * * @ param name Name of the object to remove from this session. * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> remove. Attribute ()</code> */ public void remove. Value(String name) { Session names. copy. Into(value. Names ); return value. Names ; = context. get. Session. Time. Out (); } } /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { /** * The time this session was created, in milliseconds since midnight, * January 1, 1970 GMT. */ private long creation. Time = 0 L; /** * @deprecated */ public String[] get. Value. Names () { Enumeration e = get. Attribute. Names (); Vector names = new Vector(); String[] synchronized (attributes) { Object object = attributes. get(name); if (object == null) return; attributes. remove(name); // System. out. println ( "Removing attribute " + name ); if (object instanceof Http. Session. Binding. Listener ) { (( Http. Session. Binding. Listener ) object). value. Unbound (new Http. Session. Binding. Event((Http. Session ) this, name)); } } // Unbind any objects associated with this session Vector results = new Vector(); Enumeration attrs = get. Attribute. Names (); while ( attrs. has. More. Elements ()) { String attr = (String) attrs. next. Element (); results. add. Element(attr ); } Enumeration names = results. elements(); while ( names. has. More. Elements ()) { String name = (String) names. next. Element (); remove. Attribute(name ); } // Mark this session as invalid set. Valid(false ); } Application. Session(String id, Server. Session server. Session , Context context) { this. server. Session = server. Session ; this. context = context; this. id = id; public long get. Creation. Time () { if (valid) { return creation. Time ; } else { String msg = sm. get. String("application. Session. session. ise // Remove this session from our manager's active if ((manager != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). remove(this); } // -------------------------- Instance Variables public Object get. Attribute(String name) { if (! valid) { String msg = sm. get. String("application. Session. session. ise throw new /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Unbound ()</code> on the object. * * @ param name Name of the object to remove from this session. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public void remove. Attribute(String name) { sessions /** * Construct a new Session associated with the specified Manager. * * @ param manager The manager with which this Session is associated */ public Standard. Session(Manager manager) { super(); this. manager = manager; throw new set. Attribute(name , value); } = this. Accessed. Time ; = System. current. Time. Millis (); /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. */ public void expire() { } } throw new this. last. Accessed. Time this. New =false; } values. put(name, value); /** * @deprecated */ public Object get. Value(String name) { return get. Attribute(name ); } /** * Core implementation of an application level session * * @author James Duncan Davidson [ duncan@eng. sun. com ] * @author Jason Hunter [ jch@eng. sun. com ] * @author James Todd [gonzo@eng. sun. com] */ public String get. Id () { if (valid) { return id; } else { String msg = sm. get. String("application. Session. session. ise /** * Update the accessed time information for this session. This method * should be called by the context when a request comes in for a particular * session, even if the application does not reference it. */ public void access() { // ----------------------------- Constructors } if ( this. inactive. Interval // ------------------------Session Public Methods // remove any existing binding if (value != null && value instanceof Http. Session. Binding. Listener ) { Http. Session. Binding. Event e = new Http. Session. Binding. Event(this , name); package org. apache. tomcat. session; this. inactive. Interval ; /** * Standard implementation of the <b>Session</b> interface. This object is * serializable , so that it can be stored in persistent storage or transferred * to a different JVM for distributable session support. * <p> * <b>IMPLEMENTATION NOTE</b>: An instance of this class represents both the * internal (Session) and application level (Http. Session ) view of the session. * However, because the class itself is not declared public, Java logic outside * of the <code>org. apache. tomcat. session</code> package cannot cast an * Http. Session view of this instance back to a Session view. * * @author Craig R. Mc. Clanahan * @version $Revision: 1. 2 $ $Date: 2000/05/15 17: 54: 10 $ */ "); Illegal. Argument. Exception(msg ); } remove. Value(name ); * Bind an object to this session, using the specified name. If an object * of the same name is already bound to this session, the object is * replaced. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Bound ()</code> on the object. * * @ param name Name to which the object is bound, cannot be null * @ param value Object to be bound, cannot be null * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> set. Attribute ()</code> */ public void put. Value(String name, Object value) { return (( Http. Session ) this); } } import org. apache. tomcat. core. *; import org. apache. tomcat. util. String. Manager ; import java. io. *; import java. net. *; import java. util. *; import javax. servlet. http. *; public class Application. Session java. io. IOException ; java. io. Object. Input. Stream ; java. io. Object. Output. Stream ; java. io. Serializable ; java. util. Enumeration; java. util. Hashtable ; java. util. Vector; javax. servlet. Servlet. Exception ; javax. servlet. http. Http. Session. Binding. Event ; javax. servlet. http. Http. Session. Binding. Listener javax. servlet. http. Http. Session. Context ; org. apache. tomcat. catalina. *; org. apache. tomcat. util. String. Manager ; Illegal. State. Exception(msg ); } } throw new /** * Return the <code> Http. Session </code> for which this object * is the facade. */ public Http. Session get. Session () { ); } * 60);

problems like… session tracking is not modularized HTTPRequest get. Cookies() get. Request. URI()(doc) get.

problems like… session tracking is not modularized HTTPRequest get. Cookies() get. Request. URI()(doc) get. Session() get. Requested. Session. Id(). . . Session. Interceptor request. Map(request) before. Body(req, resp). . . Session HTTPResponse get. Attribute(name) set. Attribute(name, val) invalidate(). . . get. Request() set. Content. Type(content. Type) get. Outptut. Stream() set. Session. Id(id). . . 7 Servlet CASCON '04

the problem of crosscutting concerns • critical aspects of large systems don’t fit in

the problem of crosscutting concerns • critical aspects of large systems don’t fit in traditional modules – logging, error handling – synchronization – security – power management – memory management – performance optimizations • tangled code has a cost logging, security, optimizations – difficult to understand – difficult to change – increases with size of system – maintenance costs are huge • good programmers work hard to get rid of tangled code – the last 10% of the tangled code causes 90% of the 8 development and maintenance headaches CASCON '04

the AOP idea aspect-oriented programming • crosscutting is inherent in complex systems • crosscutting

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 9 • Aspect-Oriented Software Development: AO support throughout lifecycle CASCON '04

this tutorial is about. . . • using AOP and Aspect. J to: –

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

language support to… App ca on. Sess on Application. Session /* * ================================== *

language support to… App ca on. Sess on Application. Session /* * ================================== * * The Apache Software License, Version 1. 1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement : * "This product includes software developed by the * Apache Software Foundation (http: //www. apache. org/). " * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache. org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http: //www. apache. org/>. * * [Additional notices, if required by prior licensing conditions] * */ public void invalidate() { server. Session. remove. Application. Session(context Standard. Session S andard. Sess on package org. apache. tomcat. session; // remove everything in the session Enumeration enum = values. keys(); while ( enum. has. More. Elements ()) { String name = ( String)enum. next. Element (); remove. Value(name ); } valid = false; } public boolean is. New () { if (! valid) { String msg = sm. get. String("application. Session. session. ise throw new "); import import import import if ( this. Access. Time == creation. Time ) { return true; } else { return false; } /** * @deprecated */ public void put. Value(String name, Object value) { set. Attribute(name , value); } public void set. Attribute(String name, Object value) { if (! valid) { String msg = sm. get. String("application. Session. session. ise "); Illegal. State. Exception(msg ); if (name == null) { String msg = sm. get. String("application. Session. value. iae throw new final class Standard. Session implements Http. Session , Session { (( Http. Session. Binding. Listener)value). value. Bound(e ); } "); Illegal. State. Exception(msg ); } if (name == null) { String msg = sm. get. String("application. Session. value. iae "); Illegal. Argument. Exception(msg ); implements Http. Session { return values. get(name); private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session "); private Hashtable values = new Hashtable (); private String id; private Server. Session server. Session ; private Context context; private long creation. Time = System. current. Time. Millis (); ; private long this. Access. Time = creation. Time ; private long last. Accessed = creation. Time ; private int inactive. Interval = -1; private boolean valid = true; /** * The collection of user data attributes associated with this Session. */ private Hashtable attributes = new Hashtable (); /** * The session identifier of this Session. */ private String id = null; /** * Descriptive information describing this Session implementation. */ private static final String info = "Standard. Session/1. 0"; while ( e. has. More. Elements ()) { names. add. Element(e. next. Element ()); } value. Names = new String[names. size()]; != -1) { *= 60; public Enumeration get. Attribute. Names () { if (! valid) { String msg = sm. get. String("application. Session. session. ise Server. Session get. Server. Session () { return server. Session ; } throw new "); Illegal. State. Exception(msg ); } /** * The Manager with which this Session is associated. */ private Manager manager = null; /** * The maximum time interval, in seconds, between client requests before * the servlet container may invalidate this session. A negative time * indicates that the session should never time out. */ private int max. Inactive. Interval = -1; return ( Enumeration)values. Clone. keys (); } void accessed() { // set last accessed to this. Access. Time as it will be left over // from the previous access last. Accessed = this. Access. Time ; this. Access. Time = System. current. Time. Millis (); /** * @deprecated */ public void remove. Value(String remove. Attribute(name ); } validate(); name) { /** * Flag indicating whether this session is new or } public void remove. Attribute(String name) { if (! valid) { String msg = sm. get. String("application. Session. session. ise void validate() { // if we have an inactive interval, check to see if we've exceeded it if ( inactive. Interval != -1) { int this. Interval = ( int)(System. current. Time. Millis () - last. Accessed ) / 1000; throw new "); */ private boolean is. New = true; /** * Return the <code> is. Valid </code> flag for this session. */ boolean is. Valid () { // HTTP SESSION IMPLEMENTATION METHODS "); /** * The string manager for this package. */ private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. se if (o instanceof Http. Session. Binding. Listener ) { Http. Session. Binding. Event e = new Http. Session. Binding. Event(this, name ); (( Http. Session. Binding. Listener)o). value. Unbound(e ssion "); /** * The HTTP session context associated with this session. */ private static Http. Session. Context session. Context = null; } values. remove(name); } public void set. Max. Inactive. Interval(int interval) { if (! valid) { String msg = sm. get. String("application. Session. session. ise "); throw new "); /** * The current accessed time for this session. */ private long this. Accessed. Time = creation. Time ; Illegal. State. Exception(msg ); } inactive. Interval = interval; } } /** * * @deprecated */ public int get. Max. Inactive. Interval () { if (! valid) { String msg = sm. get. String("application. Session. session. ise throw new public Http. Session. Context get. Session. Context () { return new Session. Context. Impl (); } "); // ------------------------Http. Session Properties return inactive. Interval ; } } //-----------------------------------"); // Deserialize the scalar instance variables (except Manager) creation. Time = ((Long) stream. read. Object()). long. Value (); id = (String) stream. read. Object (); last. Accessed. Time = ((Long) stream. read. Object()). long. Value (); max. Inactive. Interval = ((Integer) stream. read. Object()). int. Value (); is. New = ((Boolean) stream. read. Object()). boolean. Value (); is. Valid = ((Boolean) stream. read. Object()). boolean. Value (); /** * Return the time when this session was created, in milliseconds since * midnight, January 1, 1970 GMT. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public long get. Creation. Time () { } // -------------------------- Session Properties /** * Set the creation time for this session. This method is called by the * Manager when an existing Session instance is reused. * * @ param time The new creation time */ public void set. Creation. Time(long time) { this. creation. Time = time; this. last. Accessed. Time = time; this. Accessed. Time = time; Illegal. State. Exception(msg ); } } } /** * Return the session identifier for this session. */ public String get. Id () { } // -----------------------Http. Session Public Methods } if ((this. id != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). remove(this); Server. Sess on return (attributes. keys()); } /** * Return descriptive information about this Session implementation and * the corresponding version number, in the format * <code>& lt; description> /< version&gt ; </code>. */ public String get. Info () { return (this. info); } /** * Return the object bound with the specified name in this session, or * <code>null</code> if no object is bound with that name. * * @ param name Name of the value to be returned * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> get. Attribute ()</code> */ public Object get. Value(String name) { /** * The string manager for this package. */ private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session return ( get. Attribute(name )); return ( this. last. Accessed. Time ); } Vector results = new Vector(); Enumeration attrs = get. Attribute. Names (); while ( attrs. has. More. Elements ()) { String attr = (String) attrs. next. Element (); results. add. Element(attr ); } String names[] = new String[results. size()]; for ( int i = 0; i < names. length; i++) names[i] = (String) results. element. At(i ); return (names); return (this. manager); } /** * Set the Manager within which this Session is valid. * * @ param manager The new Manager */ public void set. Manager(Manager manager) { this. manager = manager; } /** * Return the maximum time interval, in seconds, between client requests * before the servlet container will invalidate the session. A negative * time indicates that the session should never time out. * * @exception Illegal. State. Exception if this method is called on * an invalidated session */ public int get. Max. Inactive. Interval () { return ( this. max. Inactive. Interval ); } /** * Set the maximum time interval, in seconds, between client requests * before the servlet container will invalidate the session. A negative * time indicates that the session should never time out. * * @ param interval The new maximum interval */ public void set. Max. Inactive. Interval(int interval) { this. max. Inactive. Interval = interval; private Vector dummy = new Vector(); /** * Return the session identifiers of all sessions defined * within this context. * * @deprecated As of Java Servlet API 2. 1 with no replacement. * This method must return an empty <code>Enumeration</code> * and will be removed in a future version of the API. */ public Enumeration get. Ids () { } return (dummy. elements()); /** * Invalidates this session and unbinds any objects bound to it. * * @exception Illegal. State. Exception if this method is called on * an invalidated session */ public void invalidate() { /** * Return the <code> Http. Session </code> associated with the * specified session identifier. * * @ param id Session identifier for which to look up a session * * @deprecated As of Java Servlet API 2. 1 with no replacement. * This method must return null and will be removed in a * future version of the API. */ public Http. Session get. Session(String id) { // Cause this session to expire(); } return (null); /** * Return <code>true</code> if the client does not yet know about the * session, or if the client chooses not to join the session. For * example, if the server used only cookie-based sessions, and the client * has disabled the use of cookies, then a session would be new on each * request. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public boolean is. New () { return ( this. New ); } } return (null); } * should be. ())); public Http. Session create. Session(Context * return * @author Craig R. Mc. Clanahan ctx) { manager. create. Session(). get. Session (); } */ public final class Standard. Session. Manager * Remove all sessions because our associated Context is being shut down. implements Session. Manager { * ())); * @ param ctx The context that is being shut down */ // -----------------------------Constructors public void remove. Sessions(Context ctx) { // contexts, we just want to remove the sessions of Session. Manager that adapts to the corresponding if (manager */ ((Lifecycle) manager). stop(); manager = new if (manager } catch ( Lifecycle. Exception Standard. Manager (); throw new instanceof Lifecycle) { } ((Lifecycle) manager). configure(null); ((Lifecycle) manager). start(); } catch ( Lifecycle. Exception throw new } e) { Illegal. State. Exception ("" + e); /** } * Used by context to configure the session manager's inactivity timeout. * * The Session. Manager may have some default session time out, the * Context on the other hand has it's timeout set by the deployment * descriptor ( web. xml ). This method lets the Context // Validate and update our current component state if (!started) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. not. Started ")); started = false; // Stop the background reaper thread. Stop (); Server. Session. Manager Server. Sess on. Manager // Expire all active sessions Session sessions[] = find. Sessions (); for ( int i = 0; i < sessions. length; i++) { Standard. Session session = ( Standard. Session ) sessions[i]; if (! session. is. Valid ()) continue; session. expire(); } } = check. Interval ; } // ---------------------------- Private Methods /** * Return descriptive information about this Manager implementation and * the corresponding version number, in the format * <code>& lt; description> /< version&gt ; </code>. */ public String get. Info () { /** * Invalidate all sessions that have expired. */ private void process. Expires () { long time. Now = System. current. Time. Millis (); Session sessions[] = find. Sessions (); return (this. info); // XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later. package org. apache. tomcat. session; import org. apache. tomcat. util. *; import org. apache. tomcat. core. *; import java. io. *; import java. net. *; import java. util. *; import javax. servlet. http. *; synchronized void reap() { Enumeration enum = sessions. keys(); while ( enum. has. More. Elements ()) { Object key = enum. next. Element (); Server. Session session = ( Server. Session)sessions. get(key ); /** * * @author James Duncan Davidson [ duncan@eng. sun. com ] * @author Jason Hunter [ jch@eng. sun. com ] * @author James Todd [gonzo@eng. sun. com] */ public class Server. Session. Manager for ( int i = 0; i < sessions. length; i++) { Standard. Session session = ( Standard. Session ) sessions[i]; if (! session. is. Valid ()) continue; int max. Inactive. Interval = session. get. Max. Inactive. Interval (); if ( max. Inactive. Interval < 0) continue; int time. Idle = // Truncate, do not round up ( int) (( time. Now - session. get. Last. Accessed. Time ()) / 1000 L); if ( time. Idle >= max. Inactive. Interval ) session. expire(); } } /** * Return the maximum number of active Sessions allowed, or -1 for * no limit. */ public int get. Max. Active. Sessions () { return ( this. max. Active. Sessions ); } session. reap(); session. validate(); } } } implements Session. Manager { synchronized void remove. Session(Server. Session String id = session. get. Id (); private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session "); private static Server. Session. Manager manager; // = new Server. Session. Manager (); session. invalidate(); sessions. remove(id); session) { /** * Set the maximum number of actives Sessions allowed, or -1 for * no limit. * * @ param max The new maximum number of sessions */ public void set. Max. Active. Sessions(int max) { /** * Sleep for the duration specified by the <code> * property. */ private void thread. Sleep () { static { manager = new } this. max. Active. Sessions = -1; public void Enumera remove. Sessions(Context } // ----------------------------- Public Methods } /** * Construct and return a new session object, based on the default * settings specified by this Manager's properties. The session * id will be assigned by this method, and available via the get. Id () * method of the returned session. If a new session cannot be created * for any reason, return <code>null</code>. * * @exception Illegal. State. Exception if a new session cannot be * instantiated for any reason */ public Session create. Session () { /** * Start the background thread that will periodically check for * session timeouts. */ private void thread. Start () { get. Manager () { private Server. Session. Manager () { reaper = Reaper. get. Reaper (); reaper. set. Server. Session. Manager(this ); reaper. start(); } public void accessed( Context ctx, Request req, String id ) { Application. Session ap. S=(Application. Session)find. Session ( ctx, id); if( ap. S==null) return; if (( max. Active. Sessions >= 0) && (sessions. size() >= max. Active. Sessions )) throw new Illegal. State. Exception ( sm. get. String("standard. Manager. create. Session. ise Server. Session serv. S =ap. S. get. Server. Session (); serv. S. accessed (); ap. S. accessed (); } = max; context) { Server. Session. Manager (); private Hashtable sessions = new Hashtable (); private Reaper reaper; } check. Interval </code> try { Thread. sleep(check. Interval * 1000 L); } catch ( Interrupted. Exception e) { ; } } protected int inactive. Interval if (thread != null) return; thread. Done = false; thread = new Thread(this, thread. set. Daemon(true ); thread. start(); /** * Stop the background thread that is periodically checking for * session timeouts. */ private void thread. Stop () { } // cache it - no need to compute it again req. set. Session ( ap. S ); } if (thread == null) return; public Http. Session create. Session(Context ctx) { String session. Id = Session. Id. Generator. generate. Id (); Server. Session session = new Server. Session(session. Id ); sessions. put(session. Id , session); if(-1 != inactive. Interval ) { session. set. Max. Inactive. Interval(inactive. Interval thread. Name ); } ")); return ( super. create. Session ()); thread. Done = true; thread. interrupt(); try { thread. join(); } catch ( Interrupted. Exception ; } ); } return session. get. Application. Session ( ctx, true ); } e) { thread = null; public Http. Session find. Session(Context ctx, String id) { Server. Session s. Session =(Server. Session)sessions. get(id ); if(s. Session ==null) return null; } return s. Session. get. Application. Session(ctx , false); // --------------------------- Background Thread } /** * The background thread that checks for session timeouts and shutdown. */ public void run() { // Loop until the termination semaphore is set while (! thread. Done ) { thread. Sleep (); process. Expires (); } } e) { Illegal. State. Exception ("" + e); } try { } } } this. check. Interval instanceof Lifecycle) { try { public Standard. Session. Manager () { // --------------------------- Instance Variables /** * Set the check interval (in seconds) for this Manager. * * @ param check. Interval The new check interval */ public void set. Check. Interval(int check. Interval ) { ctx! // The manager will still run after that ( i. e. keep database // connection open * implementation. conforgure the * session manager according to this value. * * @ param minutes The session inactivity timeout in minutes. */ /** public void * The Manager implementation we are actually using. set. Session. Time. Out(int minutes) { if(-1 != minutes) { */ // The manager works with seconds. . . private Manager manager = null; manager. set. Max. Inactive. Interval(minutes } } { manager. find. Session(id ); session. get. Session (); } catch ( IOException e) { Session. Manager interface) * a Context to tell the Manager that we create what the default session * timeout for this web application (specified in the deployment descriptor) // Start the background reaper thread. Start (); } CASCON 04 return * XXX - At present, there is no way (via the for /** * Gracefully terminate the active use of the public methods of this * component. This method should be the last one called on a given * instance of this component. * * @exception Illegal. State. Exception if this component has not been started * @exception Illegal. State. Exception if this component has already * been stopped * @exception Lifecycle. Exception if this component detects a fatal error * that needs to be reported */ public void stop() throws Lifecycle. Exception { } 11 Session session = if(session!=null) * <p> ())); // Validate and update our current component state if (!configured) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. not. Configured ")); if (started) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. already. Started ")); started = true; return ( this. check. Interval ); public static Server. Session. Manager return manager; } } try { * collection class, and has minimal knowledge of the detailed request * Create a new Manager /** * Return the check interval (in seconds) for this Manager. */ public int get. Check. Interval () { // ------------------------------- Private Class /** * Return the Manager within which this Session is valid. */ public Manager get. Manager () { public Http. Session find. Session ( Context ctx, String id ) { * processing semantics of handling sessions. "); // ------------------------------- Properties s): s & ( int get. Max. Inactive. Interval () | long get. Creation. Time () | Object get. Attribute(String ) | Enumeration get. Attribute. Names () | String[] get. Value. Names () | void invalidate() | boolean is. New () | void remove. Attribute(String ) | void set. Attribute(String , Object)); implements Http. Session. Context // XXX should we throw exception or just return null ? ? } /** * This class is a dummy implementation of the <code> Http. Session. Context </code> * interface, to conform to the requirement that such an object be returned * when <code> Http. Session. get. Session. Context ()</code> is called. * * @author Craig R. Mc. Clanahan * * @deprecated As of Java Servlet API 2. 1 with no replacement. The * interface will be removed in a future version of this API. */ Http. Session - avoid another find req. set. Session ( session ); } Once we commit to the new Manager/Session The Tomcat. Next "Manager" interface acts more like a // XXX a manager may be shared by multiple /** * Name to register for the background thread. */ private String thread. Name = "Standard. Manager "; } final class Standard. Session. Context // cache the * <b>IMPLEMENTATION NOTE</b>: * paradigm, I would suggest moving the logic implemented here back into * the core level. /** * The background thread completion semaphore. */ private boolean thread. Done = false; } /** * Return the set of names of objects bound to this session. If there * are no such objects, a zero-length array is returned. * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> get. Attribute. Names ()</code> */ public String[] get. Value. Names () { ((Session) session). access(); org. apache. tomcat. core. Session. Manager * and lifecycle configuration is not supported. * Prepare for the beginning of active use of the public methods of this * component. This method should be called after <code>configure()</code>, * and before any of the public methods of the component are utilized. * * @exception Illegal. State. Exception if this component has not yet been * configured (if required for this component) * @exception Illegal. State. Exception if this component has already been * started * @exception Lifecycle. Exception if this component detects a fatal error * that prevents this component from being used */ public void start() throws Lifecycle. Exception { /** * The background thread. */ private Thread thread = null; } /** * Return the last time the client sent a request associated with this * session, as the number of milliseconds since midnight, January 1, 1970 * GMT. Actions that your application takes, such as getting or setting * a value associated with the session, do not affect the access time. */ public long get. Last. Accessed. Time () { instanceof Session) * <p> /** * Has this component been started yet? */ private boolean started = false; static advice(Standard. Session s): invalidate(s) { before { if (! s. is. Valid ()) throw new Illegal. State. Exception ( s. sm. get. String("standard. Session. " + this. Join. Point. method. Name + ". ise")); } } ctx, Request req, String id) { Http. Session session= find. Session(ctx , id); if( session == null) return; if (session /** /** * The maximum number of active Sessions allowed, or -1 for no limit. */ protected int max. Active. Sessions = -1; } crosscut invalidate(Standard. Session this. id = id; if ((manager != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). add(this); * @ param session The session to be marked public void accessed(Context } // Serialize the attribute count and the attribute values stream. write. Object(new Integer(results. size())); Enumeration names = results. elements(); while ( names. has. More. Elements ()) { String name = (String) names. next. Element (); stream. write. Object(name ); stream. write. Object(attributes. get(name )); } /** * Return an <code>Enumeration</code> of <code>String</code> objects * containing the names of the objects bound to this session. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public Enumeration get. Attribute. Names () { This should be Request. Interceptor. * */ import org. apache. tomcat. core. Session. Manager ; import org. apache. tomcat. util. Session. Util ; * <p> node = attributes. get. Named. Item("max. Inactive. Interval "); if (node != null) { try { set. Max. Inactive. Interval(Integer. parse. Int(node. get. Node. Value } catch ( Throwable t) { ; // XXX - Throw exception? } } /** * The descriptive information about this implementation. */ private static final String info = "Standard. Manager/1. 0"; // Accumulate the names of serializable attributes Vector results = new Vector(); Enumeration attrs = get. Attribute. Names (); while ( attrs. has. More. Elements ()) { String attr = (String) attrs. next. Element (); Object value = attributes. get(attr ); if (value instanceof Serializable ) results. add. Element(attr ); } return (attributes. get(name)); return (this. id); * Mark the specified session's last accessed time. * called for each request by a import org. apache. tomcat. core. Request; import org. apache. tomcat. core. Response; * XXX - At present, use of <code> Standard. Manager </code> is hard coded, node = attributes. get. Named. Item("max. Active. Sessions "); if (node != null) { try { set. Max. Active. Sessions(Integer. parse. Int(node. get. Node. Value } catch ( Throwable t) { ; // XXX - Throw exception? } } } /** * Set the session identifier for this session. * * @ param id The new session identifier */ public void set. Id(String id) { /** import org. apache. tomcat. catalina. *; import org. apache. tomcat. core. Context; * Specialized implementation of node = attributes. get. Named. Item("check. Interval "); if (node != null) { try { set. Check. Interval(Integer. parse. Int(node. get. Node. Value } catch ( Throwable t) { ; // XXX - Throw exception? } } /** * Has this component been configured yet? */ private boolean configured = false; // Write the scalar instance variables (except Manager) stream. write. Object(new Long(creation. Time )); stream. write. Object(id ); stream. write. Object(new Long(last. Accessed. Time )); stream. write. Object(new Integer(max. Inactive. Interval )); stream. write. Object(new Boolean(is. New )); stream. write. Object(new Boolean(is. Valid )); /** * Return the object bound with the specified name in this session, or * <code>null</code> if no object is bound with that name. * * @ param name Name of the attribute to be returned * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public Object get. Attribute(String name) { import javax. servlet. http. Cookie ; import javax. servlet. http. Http. Session ; * that adapts to the new component-based Manager implementation. ")); // Parse and process our configuration parameters if (!(" Manager". equals(parameters. get. Node. Name ()))) return; Named. Node. Map attributes = parameters. get. Attributes (); Node node = null; /** * The interval (in seconds) between checks for expired sessions. */ private int check. Interval = 60; /** * Write a serialized version of this session object to the specified * object output stream. * <p> * <b>IMPLEMENTATION NOTE</b>: The owning Manager will not be stored * in the serialized representation of this Session. After calling * <code> read. Object ()</code>, you must set the associated Manager * explicitly. * <p> * <b>IMPLEMENTATION NOTE</b>: Any attribute that is not Serializable * will be silently ignored. If you do not want any such attributes, * be sure the <code>distributable</code> property of our associated * Manager is set to <code>true</code>. * * @ param stream The output stream to write to * * @exception IOException if an input/output error occurs */ private void write. Object(Object. Output. Stream stream) throws IOException { if ( session. Context == null) session. Context = new Standard. Session. Context (); return ( session. Context ); // Validate and update our current component state if (configured) throw new Lifecycle. Exception ( sm. get. String("standard. Manager. already. Configured configured = true; if (parameters == null) return; // --------------------------- Instance Variables } /** * Return the session context with which this session is associated. * * @deprecated As of Version 2. 1, this method is deprecated and has no * replacement. It will be removed in a future version of the * Java Servlet API. */ public Http. Session. Context get. Session. Context () { import java. io. IOException ; /** * Configure this component, based on the specified configuration * parameters. This method should be called immediately after the * component instance is created, and before <code>start()</code> * is called. * * @ parameters Configuration parameters for this component * (<B>FIXME: What object type should this really be? ) * * @exception Illegal. State. Exception if this component has already been * configured and/or started * @exception Lifecycle. Exception if this component detects a fatal error * in the configuration parameters it was given */ public void configure(Node parameters) throws Lifecycle. Exception { public final class Standard. Manager extends Manager. Base implements Lifecycle, Runnable { // Deserialize the attribute count and attribute values int n = ((Integer) stream. read. Object()). int. Value (); for ( int i = 0; i < n; i++) { String name = (String) stream. read. Object (); Object value = (Object) stream. read. Object (); attributes. put(name, value); } return ( this. creation. Time ); Illegal. State. Exception(msg ); } Http. Session Private Methods /** * Read a serialized version of this session object from the specified * object input stream. * <p> * <b>IMPLEMENTATION NOTE</b>: The reference to the owning Manager * is not restored by this method, and must be set explicitly. * * @ param stream The input stream to read from * * @exception Class. Not. Found. Exception if an unknown class is specified * @exception IOException if an input/output error occurs */ private void read. Object(Object. Input. Stream stream) throws Class. Not. Found. Exception , IOException { ); } Illegal. State. Exception(msg ); } } // ---------------------- java. io. IOException ; java. util. Enumeration; java. util. Hashtable ; java. util. Vector; org. apache. tomcat. catalina. *; javax. servlet. http. Cookie ; javax. servlet. http. Http. Session ; org. apache. tomcat. util. String. Manager ; org. w 3 c. dom. Named. Node. Map; org. w 3 c. dom. Node; /** * Standard implementation of the <b>Manager</b> interface that provides * no session persistence or distributable capabilities, but does support * an optional, configurable, maximum number of active sessions allowed. * <p> * Lifecycle configuration of this component assumes an XML node * in the following format: * <code> * & lt; Manager class. Name ="org. apache. tomcat. session. Standard. Manager " * check. Interval ="60" max. Active. Sessions ="-1" * max. Inactive. Interval ="-1" /> * </code> * where you can adjust the following parameters, with default values * in square brackets: * <ul> * <li><b>check. Interval </b> - The interval (in seconds) between background * thread checks for expired sessions. [60] * <li><b>max. Active. Sessions </b> - The maximum number of sessions allowed to * be active at once, or -1 for no limit. [-1] * <li><b>max. Inactive. Interval </b> - The default maximum number of seconds of * inactivity before which the servlet container is allowed to time out * a session, or -1 for no limit. This value should be overridden from * the default session timeout specified in the web application deployment * descriptor, if any. [-1] * </ ul> * * @author Craig R. Mc. Clanahan * @version $Revision: 1. 1 $ $Date: 2000/05/02 21: 28: 30 $ */ this. New = is. New ; } /** * Set the <code> is. Valid </code> flag for this session. * * @ param is. Valid The new value for the <code> is. Valid </code> flag */ void set. Valid(boolean is. Valid ) { } /** * Flag indicating whether this session is valid or not. */ private boolean is. Valid = false; Illegal. Argument. Exception(msg ); Object o = values. get(name); "); flag // ----------------------------- Public Methods package org. apache. tomcat. session; // --------------------------- Lifecycle Methods synchronized (attributes) { remove. Attribute(name ); attributes. put(name, value); if (value instanceof Http. Session. Binding. Listener ) (( Http. Session. Binding. Listener ) value). value. Bound (new Http. Session. Binding. Event((Http. Session ) this, name)); } /** * Set the <code> is. New </code> flag for this session. * * @ param is. New The new value for the <code> is. New </code> Standard. Session. Manager S andard. Sess on. Manager package org. apache. tomcat. session; import import import if ((manager != null) && manager. get. Distributable () && !(value instanceof Serializable )) throw new Illegal. Argument. Exception ( sm. get. String("standard. Session. set. Attribute. iae ")); return ( this. Valid ); } this. Valid = is. Valid ; } throw new } // ------------------------ Session Package Methods Standard. Manager Sess on n ercep or /** * Bind an object to this session, using the specified name. If an object * of the same name is already bound to this session, the object is * replaced. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Bound ()</code> on the object. * * @ param name Name to which the object is bound, cannot be null * @ param value Object to be bound, cannot be null * * @exception Illegal. Argument. Exception if an attempt is made to add a * non- serializable object in an environment marked distributable. * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public void set. Attribute(String name, Object value) { Illegal. State. Exception(msg ); if (name == null) { String msg = sm. get. String("application. Session. value. iae throw new remove. Attribute(name ); } not. } if ( this. Interval > inactive. Interval ) { invalidate(); } } } public long get. Last. Accessed. Time () { if (valid) { return last. Accessed ; } else { String msg = sm. get. String("application. Session. session. ise attributes. clear(); creation. Time = 0 L; id = null; last. Accessed. Time = 0 L; manager = null; max. Inactive. Interval = -1; is. New = true; is. Valid = false; // Tell our Manager that this Session has been recycled if ((manager != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). recycle(this); */ void set. New(boolean is. New ) { Hashtable values. Clone = ( Hashtable)values. clone (); /** * Called by context when request comes in so that accesses and * inactivities can be dealt with accordingly. */ throw new // Reset the instance variables associated with this /** * The last accessed time for this Session. */ private long last. Accessed. Time = creation. Time ; } } /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Unbound ()</code> on the object. * * @ param name Name of the object to remove from this session. * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> remove. Attribute ()</code> */ public void remove. Value(String name) { Session names. copy. Into(value. Names ); return value. Names ; = context. get. Session. Time. Out (); } } /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { /** * The time this session was created, in milliseconds since midnight, * January 1, 1970 GMT. */ private long creation. Time = 0 L; /** * @deprecated */ public String[] get. Value. Names () { Enumeration e = get. Attribute. Names (); Vector names = new Vector(); String[] synchronized (attributes) { Object object = attributes. get(name); if (object == null) return; attributes. remove(name); // System. out. println ( "Removing attribute " + name ); if (object instanceof Http. Session. Binding. Listener ) { (( Http. Session. Binding. Listener ) object). value. Unbound (new Http. Session. Binding. Event((Http. Session ) this, name)); } } // Unbind any objects associated with this session Vector results = new Vector(); Enumeration attrs = get. Attribute. Names (); while ( attrs. has. More. Elements ()) { String attr = (String) attrs. next. Element (); results. add. Element(attr ); } Enumeration names = results. elements(); while ( names. has. More. Elements ()) { String name = (String) names. next. Element (); remove. Attribute(name ); } // Mark this session as invalid set. Valid(false ); } Application. Session(String id, Server. Session server. Session , Context context) { this. server. Session = server. Session ; this. context = context; this. id = id; public long get. Creation. Time () { if (valid) { return creation. Time ; } else { String msg = sm. get. String("application. Session. session. ise // Remove this session from our manager's active if ((manager != null) && (manager instanceof Manager. Base )) (( Manager. Base ) manager). remove(this); } // -------------------------- Instance Variables public Object get. Attribute(String name) { if (! valid) { String msg = sm. get. String("application. Session. session. ise throw new /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Unbound ()</code> on the object. * * @ param name Name of the object to remove from this session. * * @exception Illegal. State. Exception if this method is called on an * invalidated session */ public void remove. Attribute(String name) { sessions /** * Construct a new Session associated with the specified Manager. * * @ param manager The manager with which this Session is associated */ public Standard. Session(Manager manager) { super(); this. manager = manager; throw new set. Attribute(name , value); } = this. Accessed. Time ; = System. current. Time. Millis (); /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. */ public void expire() { } } throw new this. last. Accessed. Time this. New =false; } values. put(name, value); /** * @deprecated */ public Object get. Value(String name) { return get. Attribute(name ); } /** * Core implementation of an application level session * * @author James Duncan Davidson [ duncan@eng. sun. com ] * @author Jason Hunter [ jch@eng. sun. com ] * @author James Todd [gonzo@eng. sun. com] */ public String get. Id () { if (valid) { return id; } else { String msg = sm. get. String("application. Session. session. ise /** * Update the accessed time information for this session. This method * should be called by the context when a request comes in for a particular * session, even if the application does not reference it. */ public void access() { // ----------------------------- Constructors } if ( this. inactive. Interval // ------------------------Session Public Methods // remove any existing binding if (value != null && value instanceof Http. Session. Binding. Listener ) { Http. Session. Binding. Event e = new Http. Session. Binding. Event(this , name); package org. apache. tomcat. session; this. inactive. Interval ; /** * Standard implementation of the <b>Session</b> interface. This object is * serializable , so that it can be stored in persistent storage or transferred * to a different JVM for distributable session support. * <p> * <b>IMPLEMENTATION NOTE</b>: An instance of this class represents both the * internal (Session) and application level (Http. Session ) view of the session. * However, because the class itself is not declared public, Java logic outside * of the <code>org. apache. tomcat. session</code> package cannot cast an * Http. Session view of this instance back to a Session view. * * @author Craig R. Mc. Clanahan * @version $Revision: 1. 2 $ $Date: 2000/05/15 17: 54: 10 $ */ "); Illegal. Argument. Exception(msg ); } remove. Value(name ); * Bind an object to this session, using the specified name. If an object * of the same name is already bound to this session, the object is * replaced. * <p> * After this method executes, and if the object implements * <code> Http. Session. Binding. Listener </code>, the container calls * <code> value. Bound ()</code> on the object. * * @ param name Name to which the object is bound, cannot be null * @ param value Object to be bound, cannot be null * * @exception Illegal. State. Exception if this method is called on an * invalidated session * * @deprecated As of Version 2. 2, this method is replaced by * <code> set. Attribute ()</code> */ public void put. Value(String name, Object value) { return (( Http. Session ) this); } } import org. apache. tomcat. core. *; import org. apache. tomcat. util. String. Manager ; import java. io. *; import java. net. *; import java. util. *; import javax. servlet. http. *; public class Application. Session java. io. IOException ; java. io. Object. Input. Stream ; java. io. Object. Output. Stream ; java. io. Serializable ; java. util. Enumeration; java. util. Hashtable ; java. util. Vector; javax. servlet. Servlet. Exception ; javax. servlet. http. Http. Session. Binding. Event ; javax. servlet. http. Http. Session. Binding. Listener javax. servlet. http. Http. Session. Context ; org. apache. tomcat. catalina. *; org. apache. tomcat. util. String. Manager ; Illegal. State. Exception(msg ); } } throw new /** * Return the <code> Http. Session </code> for which this object * is the facade. */ public Http. Session get. Session () { ); } * 60);

Aspect. J™ is… • a small and well-integrated extension to Java™ – outputs. class

Aspect. J™ is… • a small and well-integrated extension to Java™ – outputs. class files compatible with any JVM – all Java programs are Aspect. J programs • a general-purpose AO language – just as Java is a general-purpose OO language • includes IDE support – emacs, JBuilder, Forte 4 J, Eclipse • freely available implementation – compiler is Open Source • active user community – aspectj-users@eclipse. org 12 CASCON '04

Aspect. J applied to a large middleware system • • java code base with

Aspect. J applied to a large middleware system • • java code base with 10, 000 files and 500 developers Aspect. J captured logging, error handling, and profiling policies – Packaged as extension to Java language – Compatible with existing code base and platform existing policy implementations policies implemented with Aspect. J • affect every file • – 5 -30 page policy documents – applied by developers • affect every developer – policy captured explicitly – applies policy uniformly for all time • – must understand policy document • • repeat for new code assets awkward to support variants 13 don’t even think about changing the policy CASCON '04 written by central team – no burden on other 492 developers • • – complicates product line • one reusable crosscutting module automatically applied to new code easy plug and unplug – simplifies product line issues • changes to policy happen in one place

looking ahead problem structure examples: crosscutting in the design, and how to use Aspect.

looking ahead problem structure examples: crosscutting in the design, and how to use Aspect. J to capture that Aspect. J language mechanisms: crosscutting in the code mechanisms Aspect. J provides 14 CASCON '04

Part II tutorial CASCON '04

Part II tutorial CASCON '04

language mechanisms • goal: present basic mechanisms – using one simple example • emphasis

language mechanisms • goal: present basic mechanisms – using one simple example • emphasis on what the mechanisms do • small scale motivation • later – environment, tools – larger examples, design and SE issues 16 CASCON '04

basic mechanisms • 1 overlay onto Java – dynamic join points • “points in

basic mechanisms • 1 overlay onto Java – dynamic join points • “points in the execution” of Java programs • 4 small additions to Java – pointcuts • pick out join points and values at those points – primitive, user-defined pointcuts – advice • additional action to take at join points in a pointcut – inter-type declarations (aka “open classes”) – aspect • a modular unit of crosscutting behavior – comprised of advice, inter-type, pointcut, field, constructor, and method declarations 17 CASCON '04

a simple figure editor factory methods Display * Figure. Element Figure make. Point(. .

a simple figure editor factory methods Display * Figure. Element Figure make. Point(. . ) make. Line(. . ) Point 18 get. X() get. Y() set. X(int) set. Y(int) move. By(int, int) CASCON '04 move. By(int, int) 2 Line get. P 1() get. P 2() set. P 1(Point) set. P 2(Point) move. By(int, int) operations that move elements

a simple figure editor class Line implements Figure. Element{ private Point p 1, p

a simple figure editor class Line implements Figure. Element{ private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) {. . . } } class Point implements Figure. Element { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; } void set. Y(int y) { this. y = y; } void move. By(int dx, int dy) {. . . } } 19 CASCON '04

display updating • collection of figure elements – – that move periodically must refresh

display updating • collection of figure elements – – that move periodically must refresh the display as needed complex collection asynchronous events • other examples – session liveness – value caching 20 CASCON '04 we will initially assume just a single display

join points key points in dynamic call graph imagine l. move. By(2, 2) a

join points key points in dynamic call graph imagine l. move. By(2, 2) a Line dispatch a Point a method execution returning or throwing a method call returning or throwing a method execution returning or throwing 21 CASCON '04 dispatch

join point terminology a Line dispatch method execution join points • several kinds of

join point terminology a Line dispatch method execution join points • several kinds of join points – – – 22 CASCON '04 method & constructor call method & constructor execution field get & set exception handler execution static & dynamic initialization method call join points

join point terminology key points in dynamic call graph imagine l. move. By(2, 2)

join point terminology key points in dynamic call graph imagine l. move. By(2, 2) a Point a Line a Point all join points on this slide are within the control flow of this join point 23 CASCON '04

primitive pointcuts “a means of identifying join points” a pointcut is a kind of

primitive pointcuts “a means of identifying join points” a pointcut is a kind of predicate on join points that: – can match or not match any given join point and – optionally, can pull out some of the values at that join point call(void Line. set. P 1(Point)) matches if the join point is a method call with this signature 24 CASCON '04

pointcut composition pointcuts compose like predicates, using &&, || and ! a “void Line.

pointcut composition pointcuts compose like predicates, using &&, || and ! a “void Line. set. P 1(Point)” call or call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)); a “void Line. set. P 2(Point)” call whenever a Line receives a “void set. P 1(Point)” or “void set. P 2(Point)” method call 25 CASCON '04

user-defined pointcuts defined using the pointcut construct user-defined (aka named) pointcuts – can be

user-defined pointcuts defined using the pointcut construct user-defined (aka named) pointcuts – can be used in the same way as primitive pointcuts name parameters pointcut move(): call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)); more on parameters and how pointcut can expose values at join points in a few slides 26 CASCON '04

pointcuts user-defined pointcut move(): call(void Line. set. P 1(Point)) || call(void Line. set. P

pointcuts user-defined pointcut move(): call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)); primitive pointcut, can also be: - call, execution - this, target - get, set - within, withincode - handler - cflow, cflowbelow - initialization, staticinitialization 27 CASCON '04

after advice action to take after computation under join points after advice runs “on

after advice action to take after computation under join points after advice runs “on the way back out” pointcut move(): call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)); after() returning: move() { <code here runs after each move> } 28 CASCON '04 a Line

a simple aspect Display. Updating v 1 an aspect defines a special class that

a simple aspect Display. Updating v 1 an aspect defines a special class that can crosscut other classes aspect Display. Updating { pointcut move(): call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)); after() returning: move() { Display. update(); } } 29 box means complete running code CASCON '04

without Aspect. J Display. Updating v 1 class Line { private Point p 1,

without Aspect. J Display. Updating v 1 class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; Display. update(); } void set. P 2(Point p 2) { this. p 2 = p 2; Display. update(); } } • what you would expect – update calls are tangled through the code – “what is going on” is less explicit 30 CASCON '04

pointcuts can cut across multiple classes pointcut move(): call(void Line. set. P 1(Point)) ||

pointcuts can cut across multiple classes pointcut move(): call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int)); 31 CASCON '04

pointcuts can use interface signatures pointcut move(): call(void Figure. Element. move. By(int, int)) ||

pointcuts can use interface signatures pointcut move(): call(void Figure. Element. move. By(int, int)) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int)); 32 CASCON '04

a multi-class aspect Display. Updating v 2 aspect Display. Updating { pointcut move(): call(void

a multi-class aspect Display. Updating v 2 aspect Display. Updating { pointcut move(): call(void Figure. Element. move. By(int, int)) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int)); after() returning: move() { Display. update(); } 33 } CASCON '04

using values at join points demonstrate first, explain in detail afterwards • pointcut can

using values at join points demonstrate first, explain in detail afterwards • pointcut can explicitly expose certain values • advice can use those values parameter mechanism being used pointcut move(Figure. Element fig. Elt): target(fig. Elt) && (call(void Figure. Element. move. By(int, int)) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int))); 34 after(Figure. Element fe) returning: move(fe) { <fe is bound to the figure element> } CASCON '04

explaining parameters… of user-defined pointcut designator • variable is bound by user-defined pointcut declaration

explaining parameters… of user-defined pointcut designator • variable is bound by user-defined pointcut declaration – pointcut supplies value for variable – value is available to all users of user-defined pointcut parameters pointcut move(Line l): target(l) && (call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point))); typed variable in place of type name 35 after(Line line) returning: move(line) { <line is bound to the line> } CASCON '04

explaining parameters… of advice • variable is bound by advice declaration – pointcut supplies

explaining parameters… of advice • variable is bound by advice declaration – pointcut supplies value for variable – value is available in advice body pointcut move(Line l): target(l) && (call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point))); advice parameters 36 typed variable in place of type name after(Line line) returning: move(line) { <line is bound to the line> } CASCON '04

explaining parameters… • value is ‘pulled’ – right to left across ‘: ’ left

explaining parameters… • value is ‘pulled’ – right to left across ‘: ’ left side : right side – from pointcuts to user-defined pointcuts – from pointcuts to advice, and then advice body pointcut move(Line l): target(l) && (call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point))); 37 after(Line line) returning: move(line) { <line is bound to the line> } CASCON '04

target primitive pointcut designator target( Type. Name | Formal. Reference ) does two things:

target primitive pointcut designator target( Type. Name | Formal. Reference ) does two things: - exposes target - predicate on join points - any join point at which target object is an instance of type name (a dynamic test) target(Point) target(Line) target(Figure. Element) “any join point” means it matches join points of all kinds • • 38 CASCON '04 method call join points method & constructor execution join points field get & set join points dynamic initialization join points

idiom for… getting target object in a polymorphic pointcut target( Supertype. Name ) &&

idiom for… getting target object in a polymorphic pointcut target( Supertype. Name ) && • does not further restrict the join points • does pick up the target object pointcut move(Figure. Element fig. Elt): target(fig. Elt) && (call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int))); after(Figure. Element fe) returning: move(fe) { <fe is bound to the figure element> } 39 CASCON '04

pointcuts can expose values at join points pointcut move(Figure. Element fig. Elt): target(fig. Elt)

pointcuts can expose values at join points pointcut move(Figure. Element fig. Elt): target(fig. Elt) && (call(void Figure. Element. move. By(int, int)) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int))); 40 CASCON '04

context & multiple classes Display. Updating v 3 aspect Display. Updating { pointcut move(Figure.

context & multiple classes Display. Updating v 3 aspect Display. Updating { pointcut move(Figure. Element fig. Elt): target(fig. Elt) && (call(void Figure. Element. move. By(int, int)) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int))); after(Figure. Element fe) returning: move(fe) { Display. update(fe); } } 41 CASCON '04

without Aspect. J class Line { private Point p 1, p 2; Point get.

without Aspect. J class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; } void set. Y(int y) { this. y = y; } void move. By(int dx, int dy) { … } 42 } CASCON '04

without Aspect. J class Line { private Point p 1, p 2; Point get.

without Aspect. J class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; Display. update(); } void set. P 2(Point p 2) { this. p 2 = p 2; Display. update(); } void move. By(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; } void set. Y(int y) { this. y = y; } void move. By(int dx, int dy) { … } 43 } CASCON '04 Display. Updating v 1

without Aspect. J class Line { private Point p 1, p 2; Point get.

without Aspect. J class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; Display. update(); } void set. P 2(Point p 2) { this. p 2 = p 2; Display. update(); } void move. By(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; Display. update(); } void set. Y(int y) { this. y = y; Display. update(); } void move. By(int dx, int dy) { … } 44 } CASCON '04 Display. Updating v 2

without Aspect. J Display. Updating v 3 class Line { private Point p 1,

without Aspect. J Display. Updating v 3 class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; Display. update(this); } void set. P 2(Point p 2) { this. p 2 = p 2; Display. update(this); } void move. By(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; Display. update(this); } void set. Y(int y) { this. y = y; Display. update(this); } void move. By(int dx, int dy) { … } 45 } CASCON '04 • no locus of “display updating” – evolution is cumbersome – changes in all classes – have to track & change all callers

with Aspect. J class Line { private Point p 1, p 2; Point get.

with Aspect. J class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; } void set. Y(int y) { this. y = y; } void move. By(int dx, int dy) { … } 46 } CASCON '04

with Aspect. J Display. Updating v 1 class Line { private Point p 1,

with Aspect. J Display. Updating v 1 class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } aspect Display. Updating { pointcut move(): call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)); void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; } void set. Y(int y) { this. y = y; } void move. By(int dx, int dy) { … } 47 } CASCON '04 after() returning: move() { Display. update(); } }

with Aspect. J Display. Updating v 2 class Line { private Point p 1,

with Aspect. J Display. Updating v 2 class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } aspect Display. Updating { pointcut move(): call(void Figure. Element. move. By(int, int) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int)); void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) { … } } class Point { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; } void set. Y(int y) { this. y = y; } void move. By(int dx, int dy) { … } 48 } CASCON '04 after() returning: move() { Display. update(); } }

with Aspect. J Display. Updating v 3 class Line { private Point p 1,

with Aspect. J Display. Updating v 3 class Line { private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } aspect Display. Updating { pointcut move(Figure. Element fig. Elt): target(fig. Elt) && (call(void Figure. Element. move. By(int, int) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int))); void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) { … } after(Figure. Element fe) returning: move(fe) { Display. update(fe); } } class Point } { private int x = 0, y = 0; int get. X() { return x; } int get. Y() { return y; } void set. X(int x) { this. x = x; } void set. Y(int y) { this. y = y; } void move. By(int dx, int dy) { … } 49 } CASCON '04 • clear display updating module – all changes in single aspect – evolution is modular

aspects crosscut classes aspect modularity cuts across class modularity Display * Figure. Element Figure

aspects crosscut classes aspect modularity cuts across class modularity Display * Figure. Element Figure make. Point(. . ) make. Line(. . ) Point 50 get. X() get. Y() set. X(int) set. Y(int) move. By(int, int) CASCON '04 move. By(int, int) 2 Line get. P 1() get. P 2() set. P 1(Point) set. P 2(Point) move. By(int, int) Display. Updating

advice is • before additional action to take at join points before proceeding at

advice is • before additional action to take at join points before proceeding at join point • after returning a value at join point • after throwing a throwable at join point • after returning at join point either way • around 51 CASCON '04 on arrival at join point gets explicit control over when&if program proceeds

contract checking simple example of before/after/around • pre-conditions – check whether parameter is valid

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 52 CASCON '04

pre-condition using before advice aspect Point. Bounds. Pre. Condition { before(int new. X): call(void

pre-condition using before advice aspect Point. Bounds. Pre. Condition { before(int new. X): call(void Point. set. X(int)) && args(new. X) { assert new. X >= MIN_X; what follows the ‘: ’ is assert new. X <= MAX_X; always a pointcut – } primitive or user-defined before(int new. Y): call(void Point. set. Y(int)) && args(new. Y) { assert new. Y >= MIN_Y; assert new. Y <= MAX_Y; } } 53 CASCON '04

post-condition using after advice aspect Point. Bounds. Post. Condition { after(Point p, int new.

post-condition using after advice aspect Point. Bounds. Post. Condition { after(Point p, int new. X) returning: call(void Point. set. X(int)) && target(p) && args(new. X) { assert p. get. X() == new. X; } after(Point p, int new. Y) returning: call(void Point. set. Y(int)) && target(p) && args(new. Y) { assert p. get. Y() == new. Y; } } 54 CASCON '04

condition enforcement using around advice aspect Point. Bounds. Enforcement { void around(int new. X):

condition enforcement using around advice aspect Point. Bounds. Enforcement { void around(int new. X): call(void Point. set. X(int)) && args(new. X) { proceed( clip(new. X, MIN_X, MAX_X) ); } void around(int new. Y): call(void Point. set. Y(int)) && args(new. Y) { proceed( clip(new. Y, MIN_Y, MAX_Y) ); } private int clip(int val, int min, int max) { return Math. max(min, Math. min(max, val)); } } 55 CASCON '04

special method for each around advice with the signature Return. Type around(T 1 arg

special method for each around advice with the signature Return. Type around(T 1 arg 1, T 2 arg 2, …) there is a special method with the signature Return. Type proceed(T 1, T 2, …) available only in around advice means “run what would have run if this around advice had not been defined” 56 CASCON '04

extra: caching using around advice aspect Point. Caching { private My. Lookup. Table cache

extra: caching using around advice aspect Point. Caching { private My. Lookup. Table cache = new My. Lookup. Table(); Point around(int x, int y): call(Point. new(int, int)) && args(x, y) { Point ret = cache. lookup(x, y); if (ret == null) { ret = proceed(x, y); cache. add(x, y, ret); } return ret; } } 57 CASCON '04

property-based crosscutting package com. parc. scan; public class C 2 { package com. parc.

property-based crosscutting package com. parc. scan; public class C 2 { package com. parc. print; … package com. parc. copy; public int frotz() { public class C 1 { public class C 3 { A. do. Something(…); … … … public void foo() { public String s 1() { } A. do. Something(…); public int bar() { … … A. do. Something(…); } } … … … } } } … } • 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 58 CASCON '04

property-based crosscutting aspect Public. Error. Logging { Logger log = Logger. global; neatly captures

property-based crosscutting aspect Public. Error. Logging { Logger log = Logger. global; neatly captures public interface of my packages pointcut public. Interface(): call(public * com. bigboxco. . *. *(. . )); after() throwing (Error e): public. Interface() { log. warning(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 59 CASCON '04

wildcarding in pointcuts target(Point) target(graphics. geom. *) target(graphics. . *) any type in graphics.

wildcarding in pointcuts target(Point) target(graphics. geom. *) target(graphics. . *) any type in graphics. geom any type in any sub-package of graphics call(void Point. set. X(int)) call(public * Point. *(. . )) call(public * *(. . )) any public method on Point any public method on any call(void any setter Point. set. X(int)) Point. set. Y(*)) Point. set*(*)) call(Point. new(int, int)) call(new(. . )) 60 “*” is wild card “. . ” is multi-part wild card CASCON '04 any constructor

special value this. Join. Point. Signature Object[]. . . reflective* access to the join

special value this. Join. Point. Signature Object[]. . . reflective* access to the join point get. Signature() get. Args() available in any advice (also this. Join. Point. Static. Part with only the statically determinable portions) * introspective subset of reflection consistent with Java 61 CASCON '04

using this. Join. Point in highly polymorphic advice aspect Public. Error. Logging { Logger

using this. Join. Point in highly polymorphic advice aspect Public. Error. Logging { Logger log = Logger. global; pointcut public. Interface(): call(public * com. bigboxco. . *. *(. . )); after() throwing (Error e): public. Interface() { log. throwing( tjp. get. Signature(). get. Declaring. Type(). get. Name(), tjp. get. Signature(). get. Name(), e); } } please read as this. Join. Point 62 CASCON '04 using this. Join. Point makes it possible for the advice to recover information about where it is running

other primitive pointcuts this( Type. Name ) withincode( Member. Signature ) any join point

other primitive pointcuts this( Type. Name ) withincode( Member. Signature ) any join point at which currently executing object is an instance of type name currently executing code is contained within type name currently executing code is specified methods or constructors get( int Point. x ) set( int Point. x ) field reference or assignment join points 63 CASCON '04

fine-grained protection a run-time error class Figure { public Line make. Line(Line p 1,

fine-grained protection a run-time error class Figure { public Line make. Line(Line p 1, Line p 2) { new Line. . . } public Point make. Point(int x, int y) { new Point. . . } want to ensure that any creation of figure elements goes through the factory methods aspect Factory. Enforcement { pointcut illegal. New. Fig. Elt(): (call(Point. new(. . )) || call(Line. new(. . ))) && !withincode(* Figure. make*(. . )); before(): illegal. New. Fig. Elt() { throw new Error("Use factory method instead. "); } 64 } CASCON '04

fine-grained protection a compile-time error class Figure { public Line make. Line(Line p 1,

fine-grained protection a compile-time error class Figure { public Line make. Line(Line p 1, Line p 2) { new Line. . . } public Point make. Point(int x, int y) { new Point. . . } want to ensure that any creation of figure elements goes through the factory methods aspect Factory. Enforcement { pointcut illegal. New. Fig. Elt(): (call(Point. new(. . )) || call(Line. new(. . ))) && !withincode(* Figure. make*(. . )); declare error: illegal. New. Fig. Elt(): "Use factory method instead. "; } 65 } CASCON '04 must be a “static pointcut”

fine-grained protection a compile-time error class Figure { public Line make. Line(Line p 1,

fine-grained protection a compile-time error class Figure { public Line make. Line(Line p 1, Line p 2) { new Line. . . } public Point make. Point(int x, int y) { new Point. . . } want to ensure that any creation of figure elements goes through the factory methods aspect Factory. Enforcement { pointcut illegal. New. Fig. Elt(): call(Figure. Element+. new(. . )) && !withincode(* Figure. make*(. . )); declare error: illegal. New. Fig. Elt(): "Use factory method instead. "; } 66 } CASCON '04 all subtypes must be a “static pointcut”

fine-grained protection as a static inner aspect class Line implements Figure. Element{ private Point

fine-grained protection as a static inner aspect class Line implements Figure. Element{ private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) {. . . } static aspect Setter. Enforcement { declare error: set(Point Line. *) && !withincode(void Line. set. P*(Point)) "Use setter method. "; } } 67 CASCON '04

fine-grained protection as a static inner aspect class Line implements Figure. Element{ private Point

fine-grained protection as a static inner aspect class Line implements Figure. Element{ private Point p 1, p 2; Point get. P 1() { return p 1; } Point get. P 2() { return p 2; } void set. P 1(Point p 1) { this. p 1 = p 1; } void set. P 2(Point p 2) { this. p 2 = p 2; } void move. By(int dx, int dy) {. . . } static aspect Setter. Enforcement { declare error: set(Point Line. *) && !withincode(void Line. set. P*(Point)) "Use setter method, even inside Line class. "; } } 68 CASCON '04

other primitive pointcuts execution(void Point. set. X(int)) method/constructor execution join points (actual running method)

other primitive pointcuts execution(void Point. set. X(int)) method/constructor execution join points (actual running method) initialization(Point) object initialization join points staticinitialization(Point) class initialization join points (as the class is loaded) 69 CASCON '04

other primitive pointcuts cflow( Pointcut ) all join points in the dynamic control flow

other primitive pointcuts cflow( Pointcut ) all join points in the dynamic control flow of any join point picked out by Pointcut cflowbelow( Pointcut ) all join points in the dynamic control flow below any join point picked out by Pointcut 70 CASCON '04

only top-level moves Display. Updating v 4 aspect Display. Updating { pointcut move(Figure. Element

only top-level moves Display. Updating v 4 aspect Display. Updating { pointcut move(Figure. Element fe): target(fe) && (call(void Figure. Element. move. By(int, int)) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int))); pointcut top. Level. Move(Figure. Element fe): move(fe) && !cflowbelow(move(Figure. Element)); after(Figure. Element fe) returning: top. Level. Move(fe) { Display. update(fe); } } 71 CASCON '04

inter-type declarations • like member declarations. . . long void 72 CASCON '04 l

inter-type declarations • like member declarations. . . long void 72 CASCON '04 l = 37; m() {. . . }

inter-type declarations • like member declarations, but with a Target. Type long Target. Type.

inter-type declarations • like member declarations, but with a Target. Type long Target. Type. l = 37; void Target. Type. m() {. . . } 73 CASCON '04

one display per figure element Display. Updating v 5 aspect Display. Updating { private

one display per figure element Display. Updating v 5 aspect Display. Updating { private Display Figure. Element. display; static void set. Display(Figure. Element fe, Display d) { fe. display = d; } pointcut move(Figure. Element fig. Elt): <as before>; after(Figure. Element fe): move(fe) { fe. display. update(fe); } } 74 CASCON '04

field/getter/setter idiom aspect Display. Updating { private with respect to enclosing aspect declaration private

field/getter/setter idiom aspect Display. Updating { private with respect to enclosing aspect declaration private Display Figure. Element. display; public static void set. Display(Figure. Element fe, Display d) { fe. display = d; } the display field pointcut move(Figure. Element fig. Elt): <as before>; – is a field in objects of type Figure. Element, but – belongs to Display. Updating aspect after(Figure. Element fe): move(fe) should { – Display. Updating provide getter/setter fe. display. update(fe); (called by setup code) } } 75 CASCON '04

one-to-many Display. Updating v 6 aspect Display. Updating { private List Figure. Element. displays

one-to-many Display. Updating v 6 aspect Display. Updating { private List Figure. Element. displays = new Linked. List(); public static void add. Display(Figure. Element fe, Display d) { fe. displays. add(d); } public static void remove. Display(Figure. Element fe, Display d) { fe. displays. remove(d); } pointcut move(Figure. Element fig. Elt): <as before>; after(Figure. Element fe): move(fe) { Iterator iter = fe. displays. iterator(); . . . } 76 } CASCON '04

inheritance & specialization • pointcuts can have additional advice – aspect with • concrete

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 77 CASCON '04

role types and reusable aspects abstract aspect Observing { protected interface Subject { }

role types and reusable aspects abstract aspect Observing { protected interface Subject { } protected interface Observer { } private List Subject. observers = new Array. List(); public void add. Observer(Subject s, Observer o) {. . . } public void remove. Observer(Subject s, Observer o) {. . . } public static List get. Observers(Subject s) {. . . } abstract pointcut changes(Subject s); after(Subject s): changes(s) { Iterator iter = get. Observers(s). iterator(); while ( iter. has. Next() ) { notify. Observer(s, ((Observer)iter. next())); } } abstract void notify. Observer(Subject s, Observer o); 78 } CASCON '04

this is the concrete reuse Display. Updating v 7 aspect Display. Updating extends Observing

this is the concrete reuse Display. Updating v 7 aspect Display. Updating extends Observing { declare parents: Figure. Element implements Subject; declare parents: Display implements Observer; pointcut changes(Subject s): target(s) && (call(void Figure. Element. move. By(int, int)) || call(void Line. set. P 1(Point)) || call(void Line. set. P 2(Point)) || call(void Point. set. X(int)) || call(void Point. set. Y(int))); void notify. Observer(Subject s, Observer o) { ((Display)o). update(s); } } 79 CASCON '04

advice precedence • what happens if two pieces of advice apply to the same

advice precedence • what happens if two pieces of advice apply to the same join point? aspect Security { before(): call(public *(. . )) { if (!Policy. is. Allwed(tjp)) throw new Security. Exn(); } please read as this. Join. Point 80 CASCON '04 } aspect Logging { before(): logged() { System. err. println( "Entering " + tjp); } pointcut logged(): call(void troublesome. Method()); }

advice precedence • order is undefined, unless. . . – in the same aspect,

advice precedence • order is undefined, unless. . . – in the same aspect, – in subaspect, or aspect Security { before(): call(public *(. . )) – using declare if (!Policy. is. Allwed(tjp)) throw new Security. Exn(); precedence. . . { } declare precedence: Security, *; } aspect Logging { before(): logged() { System. err. println( "Entering " + tjp); } pointcut logged(): call(void troublesome. Method()); } 81 CASCON '04

summary join points method & constructor call execution field get set exception handler execution

summary join points method & constructor call execution field get set exception handler execution initialization aspects crosscutting type 82 CASCON '04 pointcuts -primitive- advice before after around call execution inter-type decls handler get set Type. field initialization Type. method() dispatch this target args declare withincode error cflowbelow parents -user-definedprecedence pointcut declaration reflection abstract this. Join. Point overriding this. Join. Point. Static. Part

where we have been… … and where we are going problem structure examples: crosscutting

where we have been… … and where we are going problem structure examples: crosscutting in the design, and how to use Aspect. J to capture that Aspect. J language mechanisms: crosscutting in the code mechanisms Aspect. J provides 83 CASCON '04

using aspects • present examples of aspects in design – intuitions for identifying aspects

using aspects • present examples of aspects in design – intuitions for identifying aspects • present implementations in Aspect. J – how the language support can help – putting Aspect. J into practice • discuss style issues – objects vs. aspects • when are aspects appropriate? 84 CASCON '04

example plug & play tracing • simple tracing – exposes join points and uses

example plug & play tracing • simple tracing – exposes join points and uses very simple advice • an unpluggable aspect – core program functionality is unaffected by the aspect 85 CASCON '04

tracing without Aspect. J class Trace. Support { Trace. Support static int TRACELEVEL =

tracing without Aspect. J class Trace. Support { Trace. Support static int TRACELEVEL = 0; static protected Print. Stream stream = null; static protected int call. Depth = -1; static void init(Print. Stream _s) {stream=_s; } static void trace. Entry(String str) { if (TRACELEVEL == 0) return; call. Depth++; print. Entering(str); } static void trace. Exit(String str) { if (TRACELEVEL == 0) return; call. Depth--; print. Exiting(str); } 86 class Point { } void set(int x, int y) { Trace. Support. trace. Entry("Point. set"); this. x = x; this. y = y; Trace. Support. trace. Exit("Point. set"); } } CASCON '04

a clear crosscutting structure Trace. Support this line is about interacting with the trace

a clear crosscutting structure Trace. Support this line is about interacting with the trace facility 87 CASCON '04 all modules of the system use the trace facility in a consistent way: entering the methods and exiting the methods

tracing as an aspect Point. Tracing { Trace. Support pointcut trace(): within(com. bigboxco. boxes.

tracing as an aspect Point. Tracing { Trace. Support pointcut trace(): within(com. bigboxco. boxes. *) && execution(* *(. . )); before(): trace() { Trace. Support. trace. Entry(tjp); } after(): trace() { Trace. Support. trace. Exit(tjp); } } 88 CASCON '04

plug and debug • plug in: • unplug: • or… 89 CASCON '04 ajc

plug and debug • plug in: • unplug: • or… 89 CASCON '04 ajc Point. java Line. java Trace. Support. java Point. Tracing. java ajc Point. java Line. java

plug and debug //From Context. Manager public void service( Request rrequest, Response rresponse )

plug and debug //From Context. Manager public void service( Request rrequest, Response rresponse ) { // log( "New request " + rrequest ); try { // System. out. print("A"); rrequest. set. Context. Manager ( this ); rrequest. set. Response(rresponse ); rresponse. set. Request(rrequest ); // wront request - parsing error int status=rresponse. get. Status(); // log( "New request " + rrequest ); // System. out. print(“A”); if( status < 400 ) status= process. Request( rrequest ); if(status==0) status=authenticate( rrequest, rresponse ); if(status == 0) status=authorize( rrequest, rresponse ); if( status == 0 ) { rrequest. get. Wrapper(). handle. Request(rrequest , rresponse); } else { // something went wrong handle. Error( rrequest, rresponse, null, status ); } } catch (Throwable t) { handle. Error( rrequest, rresponse, t, 0 ); } // System. out. print("B"); try { rresponse. finish(); rrequest. recycle(); rresponse. recycle(); } catch( Throwable ex ) { if(debug>0) log( "Error closing request " + ex); } // log( "Done with request " + rrequest ); // System. out. print("C"); return; } // System. out. print("B"); if(debug>0) log("Error closing request " + ex); // log("Done with request " + rrequest); // System. out. print("C"); 90 CASCON '04

plug and debug • turn debugging on/off without editing classes • debugging disabled with

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 91 CASCON '04

aspects in the design have these benefits • objects are no longer responsible for

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 92 CASCON '04

aspects in the code have these benefits • object code contains no calls to

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 93 CASCON '04

tracing: object vs. aspect • using an object captures tracing support, but does not

tracing: object vs. aspect • using an object captures tracing support, but does not capture its consistent usage by other objects Trace. Support 94 CASCON '04 • using an aspect captures the consistent usage of the tracing support by the objects Trace. Support

tracing using a library aspect abstract aspect Tracing { abstract pointcut trace(); aspect Big.

tracing using a library aspect abstract aspect Tracing { abstract pointcut trace(); aspect Big. Box. Co. Tracing { before(): trace() { Trace. Support. trace. Entry(tjp); } after(): trace() { Trace. Support. trace. Exit(tjp); } pointcut trace(): within(com. bigboxco. *) && execution(* *(. . )); before(): trace() { Trace. Support. trace. Entry( tjp); } after(): trace() { Trace. Support. trace. Exit( tjp); } } 95 CASCON '04 } aspect Big. Box. Co. Tracing extends Tracing { pointcut trace(): within(com. bigboxco. *) && execution(* *(. . )); }

example context-passing aspects caller 1 Service caller 2 workers need to know the caller:

example context-passing aspects caller 1 Service caller 2 workers need to know the caller: • capabilities • charge backs • to customize result worker 1 96 CASCON '04 worker 2 worker 3

context-passing aspects caller 1 Service caller 2 workers need to know the caller: •

context-passing aspects caller 1 Service caller 2 workers need to know the caller: • capabilities • charge backs • to customize result worker 1 97 CASCON '04 worker 2 worker 3

context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service. do. Service(String)); 98 CASCON '04

context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service. do. Service(String)); 98 CASCON '04

context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service. do. Service(String)); pointcut work. Points(Worker

context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service. do. Service(String)); pointcut work. Points(Worker w): target(w) && call(void Worker. do. Task(Task)); 99 CASCON '04

context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service. do. Service(String)); pointcut work. Points(Worker

context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service. do. Service(String)); pointcut work. Points(Worker w): target(w) && call(void Worker. do. Task(Task)); pointcut per. Caller. Work(Caller c, Worker w): cflow(invocations(c)) && work. Points(w); 100 CASCON '04

context-passing aspects abstract aspect Capability. Checking { pointcut invocations(Caller c): this(c) && call(void Service.

context-passing aspects abstract aspect Capability. Checking { pointcut invocations(Caller c): this(c) && call(void Service. do. Service(String)); pointcut work. Points(Worker w): target(w) && call(void Worker. do. Task(Task)); pointcut per. Caller. Work(Caller c, Worker w): cflow(invocations(c)) && work. Points(w); before (Caller c, Worker w): per. Caller. Work(c, w) { w. check. Capabilities(c); } } 101 CASCON '04

a few beginner mistakes • overuse • misunderstanding interactions with reflection – the call

a few beginner mistakes • overuse • misunderstanding interactions with reflection – the call pointcut captures call join points made from code, not those made reflectively – use execution to capture reflection 102 CASCON '04

a few beginner mistakes • not controlling circularity of advice – pointcuts sometimes match

a few beginner mistakes • not controlling circularity of advice – pointcuts sometimes match more than beginners expect aspect A { before(): call(String to. String()) { System. err. println(tjp); } } – use within or cflow to control circularity aspect A { before(): call(String to. String()) && !within(A) { System. err. println(tjp); } } 103 CASCON '04

summary so far • presented examples of aspects in design – intuitions for identifying

summary so far • presented examples of aspects in design – intuitions for identifying aspects • presented implementations in Aspect. J – how the language support can help • raised some style issues – objects vs. aspects 104 CASCON '04

when are aspects appropriate? • is there a concern that: – crosscuts the structure

when are aspects appropriate? • is there a concern that: – crosscuts the structure of several objects or operations – is beneficial to separate out 105 CASCON '04

… crosscutting • a design concern that involves several objects or operations • implemented

… 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. trace. Entry(“Point. set”) • try grep to find these [Griswold] – do a coordinated single thing • e. g. timing, observer pattern • harder to find these 106 CASCON '04

… beneficial to separate out • exactly the same questions as for objects •

… beneficial to separate out • exactly the same questions as for objects • 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 trace. Entry are really the same – easier to modify / extend • e. g. change the implementation of tracing • e. g. abstract aspect reuse – plug and play • e. g. tracing aspects unplugged but not deleted 107 CASCON '04

good designs summary • capture “the story” well • may lead to good implementations,

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

expected benefits of using AOP • good modularity, even in the presence of crosscutting

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 109 CASCON '04

Part III examples and demo CASCON '04

Part III examples and demo CASCON '04

Part IV conclusion CASCON '04

Part IV conclusion CASCON '04

AOSD • language design – more dynamic crosscuts, type system … • tools –

AOSD • language design – more dynamic crosscuts, type system … • tools – more IDE support, aspect discovery, refactoring, recutting, crosscutting views… • software engineering – UML extension, finding aspects, … • metrics – measurable benefits, areas for improvement • theory – type system for crosscutting, faster compilation, advanced crosscut constructs, modularity principles • see also aosd. net 112 CASCON '04

Aspect. J technology • Aspect. J is a small extension to Java – valid

Aspect. J technology • Aspect. J is a small extension to Java – valid Java programs are also valid Aspect. J programs • Aspect. J has its own compiler, ajc – runs on Java 2 platform (Java 1. 3 or later) – produces Java platform-compatible. class files (Java 1. 1 - 1. 4) • Aspect. J tools support – IDE extensions: Emacs, JBuilder, Net. Beans, Eclipse – ant tasks – works with existing debuggers • license – compiler, runtime and tools are Open Source and free for any use 113 CASCON '04

Aspect. J on the web • eclipse. org/aspectj – documentation – downloads – user

Aspect. J on the web • eclipse. org/aspectj – documentation – downloads – user mailing list – developer mailing list – pointers elsewhere. . . 114 CASCON '04

summary • functions OOP AOP – handles greater complexity, provides more flexibility… – crosscutting

summary • functions OOP AOP – handles greater complexity, provides more flexibility… – crosscutting modularity • Aspect. J – incremental adoption package revolutionary benefits – free Aspect. J tools – community – training, consulting, and support for use 115 CASCON '04

credits Aspect. J is now* an Eclipse project with notable work by Ron Bodkin,

credits Aspect. J is now* an Eclipse project with notable work by Ron Bodkin, Andy Clement, Adrian Colyer, Erik Hilsdale, Jim Hugunin, Wes Isberg, Mik Kersten, Gregor Kiczales slides, compiler, tools & documentation are available at eclipse. org/aspectj * Originally developed at PARC, with support from NIST and DARPA. 116 CASCON '04