Aspect-Oriented Programming with AspectJ

Aspect-Oriented Programming with AspectJ

Aspect-Oriented Programming with AspectJ Julie Waterhouse and Mik Kersten eclipse.org/aspectj IBM UBC outline I AOP and AspectJ overview problems, basic concepts, context II AspectJ 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 of code nicely fits in one box 3 CASCON '04 XML parsing 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 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 ApplicationSession /* * ==================================================================== * * 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 [email protected]

* * 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, INDIRECT, 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 * . * * [Additional notices, if required by prior licensing conditions] * */ public void invalidate() { serverSession.removeApplicationSession(context); // remove everything in the session Enumeration enum = values.keys(); while (enum.hasMoreElements()) { String name = (String)enum.nextElement(); removeValue(name); } valid = false; } public boolean isNew() { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); StandardSession package org.apache.tomcat.session; import import import import import import import import import

import import import import import java.io.IOException; java.io.ObjectInputStream; java.io.ObjectOutputStream; java.io.Serializable; java.util.Enumeration; java.util.Hashtable; java.util.Vector; javax.servlet.ServletException; javax.servlet.http.HttpSession; javax.servlet.http.HttpSessionBindingEvent; javax.servlet.http.HttpSessionBindingListener; javax.servlet.http.HttpSessionContext; org.apache.tomcat.catalina.*; org.apache.tomcat.util.StringManager; throw new IllegalStateException(msg); } if (thisAccessTime == creationTime) { return true; } else { return false; } } /** * @deprecated */ public void putValue(String name, Object value) { setAttribute(name, value); } public void setAttribute(String name, Object value) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); throw new IllegalStateException(msg); } if (name == null) { String msg = sm.getString("applicationSession.value.iae"); } throw new IllegalArgumentException(msg); removeValue(name); /** * Standard implementation of the Session interface. This object is * serializable, so that it can be stored in persistent storage or transferred * to a different JVM for distributable session support. *

* IMPLEMENTATION NOTE: An instance of this class represents both the * internal (Session) and application level (HttpSession) view of the session. * However, because the class itself is not declared public, Java logic outside * of the org.apache.tomcat.session package cannot cast an * HttpSession view of this instance back to a Session view. * * @author Craig R. McClanahan * @version $Revision: 1.2 $ $Date: 2000/05/15 17:54:10 $ */ ((HttpSessionBindingListener)value).valueBound(e); } values.put(name, value); /** * @deprecated */ public Object getValue(String name) { return getAttribute(name); } public Object getAttribute(String name) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); /** * Core implementation of an application level session * * @author James Duncan Davidson [[email protected]] * @author Jason Hunter [[email protected]] * @author James Todd [[email protected]] */ throw new IllegalStateException(msg); } throw new IllegalArgumentException(msg); public class ApplicationSession implements HttpSession { return values.get(name); private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private Hashtable values = new Hashtable(); private String id; private ServerSession serverSession; private Context context; private long creationTime = System.currentTimeMillis();; private long thisAccessTime = creationTime; private long lastAccessed = creationTime; private int inactiveInterval = -1; private boolean valid = true;

} while (e.hasMoreElements()) { names.addElement(e.nextElement()); } String[] valueNames = new String[names.size()]; ServerSession getServerSession() { return serverSession; throw new IllegalStateException(msg); } } } // Mark this session as invalid setValid(false); /** * 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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueUnbound() on the object. * * @param name Name of the object to remove from this session. * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * removeAttribute() */ public void removeValue(String name) { } super(); this.manager = manager; } // ----------------------------------------------------Instance Variables /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { // Reset the instance variables associated with this Session

/** * The collection of user data attributes associated with this Session. */ private Hashtable attributes = new Hashtable(); attributes.clear(); creationTime = 0L; id = null; lastAccessedTime = 0L; manager = null; maxInactiveInterval = -1; isNew = true; isValid = false; /** * The time this session was created, in milliseconds since midnight, * January 1, 1970 GMT. */ private long creationTime = 0L; removeAttribute(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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueBound() 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 IllegalArgumentException if an attempt is made to add a * non-serializable object in an environment marked distributable. * @exception IllegalStateException if this method is called on an * invalidated session */ public void setAttribute(String name, Object value) { // ------------------------------------------------ Session Package Methods /** * Return the isValid flag for this session. */ boolean isValid() { /** * Descriptive information describing this Session implementation. */

private static final String info = "StandardSession/ 1.0"; Sess on n ercep or } // Tell our Manager that this Session has been recycled if ((manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).recycle(this); } /** * The session identifier of this Session. */ private String id = null; return (this.isValid); /** * The last accessed time for this Session. */ private long lastAccessedTime = creationTime; if ((manager != null) && manager.getDistributable() && !(value instanceof Serializable)) throw new IllegalArgumentException (sm.getString("standardSession.setAttribute.iae")); /** * Set the isNew flag for this session. * * @param isNew The new value for the isNew flag */ void setNew(boolean isNew) { /** * The Manager with which this Session is associated. */ private Manager manager = null; synchronized (attributes) { removeAttribute(name); attributes.put(name, value); if (value instanceof HttpSessionBindingListener) ((HttpSessionBindingListener) value).valueBound (new HttpSessionBindingEvent((HttpSession) this, name)); } return (Enumeration)valuesClone.keys(); } void accessed() { // set last accessed to thisAccessTime as it will be left over

// from the previous access lastAccessed = thisAccessTime; thisAccessTime = System.currentTimeMillis(); /** * @deprecated */ this.isNew = isNew; } } /** * 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 maxInactiveInterval = -1; /** * Set the isValid flag for this session. * * @param isValid The new value for the isValid flag public void removeValue(String name) { removeAttribute(name); validate(); this.isValid = isValid; /** * Flag indicating whether this session is new or } public void removeAttribute(String name) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); not. */ private boolean isNew = true; throw new IllegalStateException(msg); } if (thisInterval > inactiveInterval) { invalidate(); } /** * Flag indicating whether this session is valid or

if (name == null) { String msg = sm.getString("applicationSession.value.iae"); } throw new IllegalArgumentException(msg); } not. */ private boolean isValid = false; } // HTTP SESSION IMPLEMENTATION METHODS Object o = values.get(name); public String getId() { if (valid) { return id; } else { String msg = sm.getString("applicationSession.session.ise"); if (o instanceof HttpSessionBindingListener) { HttpSessionBindingEvent e = new HttpSessionBindingEvent(this,name); /** * Read a serialized version of this session object from the specified * object input stream. *

* IMPLEMENTATION NOTE: 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 ClassNotFoundException if an unknown class is specified * @exception IOException if an input/output error occurs */ private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException { } } void validate() { // if we have an inactive interval, check to see if we've exceeded it if (inactiveInterval != -1) { int thisInterval = (int)(System.currentTimeMillis() - lastAccessed) / 1000; // -------------------------------------------- HttpSession Private Methods

*/ void setValid(boolean isValid) { /** * The string manager for this package. */ private StringManager sm = // ------------------------------------------------HttpSession Properties /** * Return the time when this session was created, in milliseconds since * midnight, January 1, 1970 GMT. * * @exception IllegalStateException if this method is called on an * invalidated session */ public long getCreationTime() { // Deserialize the scalar instance variables (except Manager) creationTime = ((Long) stream.readObject()).longValue(); id = (String) stream.readObject(); lastAccessedTime = ((Long) stream.readObject()).longValue(); maxInactiveInterval = ((Integer) stream.readObject()).intValue(); isNew = ((Boolean) stream.readObject()).booleanValue(); isValid = ((Boolean) stream.readObject()).booleanValue(); StringManager.getManager("org.apache.tomcat.session"); values.remove(name); } public long getCreationTime() { if (valid) { return creationTime; } else { String msg = sm.getString("applicationSession.session.ise"); public void setMaxInactiveInterval(int interval) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); throw new IllegalStateException(msg); } throw new IllegalStateException(msg); } inactiveInterval = interval; } }

/** * * @deprecated */ public int getMaxInactiveInterval() { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); /** * The HTTP session context associated with this session. */ private static HttpSessionContext sessionContext = null; /** * The current accessed time for this session. */ private long thisAccessedTime = creationTime; public HttpSessionContext getSessionContext() { return new SessionContextImpl(); } return inactiveInterval; } } //----------------------------------------------------------------------- throw new IllegalStateException(msg); // ----------------------------------------------HttpSession Public Methods /** * 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 setCreationTime(long time) { } this.creationTime = time; this.lastAccessedTime = time; this.thisAccessedTime = time; } /** * Return the object bound with the specified name in this session, or

* null if no object is bound with that name. * * @param name Name of the attribute to be returned * * @exception IllegalStateException if this method is called on an * invalidated session */ public Object getAttribute(String name) { } return (this.id); } /** * Set the session identifier for this session. * * @param id The new session identifier */ public void setId(String id) { this.id = id; ServerSess on if ((manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).add(this); } /** * Return descriptive information about this Session implementation and * the corresponding version number, in the format * <description>/<version>. */ public String getInfo() { static advice(StandardSession s): invalidate(s) { before { if (!s.isValid()) throw new IllegalStateException (s.sm.getString("standardSession." + thisJoinPoint.methodName + ".ise")); } } return (getAttribute(name)); } } /** * 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 getLastAccessedTime() { /** * Return the set of names of objects bound to this session. If there * are no such objects, a zero-length array is returned. * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * getAttributeNames() */ public String[] getValueNames() { Vector results = new Vector(); Enumeration attrs = getAttributeNames(); while (attrs.hasMoreElements()) { String attr = (String) attrs.nextElement(); results.addElement(attr); } String names[] = new String[results.size()]; for (int i = 0; i < names.length; i++) names[i] = (String) results.elementAt(i); return (names); return (this.lastAccessedTime); } /** * Return the Manager within which this Session is valid. */ public Manager getManager() { } /** * The background thread. */ private Thread thread = null; 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 Enumeration * and will be removed in a future version of the API.

*/ public Enumeration getIds() { } return (this.manager); } /** * Invalidates this session and unbinds any objects bound to it. /** * Set the Manager within which this Session is valid. * * @param manager The new Manager */ public void setManager(Manager manager) { * * @exception IllegalStateException if this method is called on * an invalidated session */ public void invalidate() { return (dummy.elements()); } // Cause this session to expire expire(); this.manager = manager; } /** * Return the HttpSession 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 HttpSession getSession(String id) { } /** * 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 IllegalStateException if this method is called on * an invalidated session */ public int getMaxInactiveInterval() { return (this.maxInactiveInterval); /** * Return true 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 IllegalStateException if this method is called on an * invalidated session */ public boolean isNew() { } return (this.isNew); /** * 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 setMaxInactiveInterval(int interval) { this.maxInactiveInterval = interval; } return (null); } // ------------------------------------------------------------- Properties // Validate and update our current component state if (!started) throw new LifecycleException (sm.getString("standardManager.notStarted")); started = false; /**

* Return the check interval (in seconds) for this Manager. */ public int getCheckInterval() { return (this.checkInterval); // Stop the background reaper thread threadStop(); } // Expire all active sessions Session sessions[] = findSessions(); for (int i = 0; i < sessions.length; i++) { StandardSession session = (StandardSession) sessions[i]; if (!session.isValid()) continue; session.expire(); } /** * Set the check interval (in seconds) for this Manager. * * @param checkInterval The new check interval */ public void setCheckInterval(int checkInterval) { ServerSessionManager this.checkInterval = checkInterval; } } // -------------------------------------------------------- Private Methods 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.*; /** * * @author James Duncan Davidson [[email protected]] * @author Jason Hunter [[email protected]] * @author James Todd [[email protected]] */ public class ServerSessionManager implements SessionManager { // 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. /**

* Return descriptive information about this Manager implementation and * the corresponding version number, in the format * <description>/<version>. */ public String getInfo() { /** * Invalidate all sessions that have expired. */ private void processExpires() { synchronized void reap() { Enumeration enum = sessions.keys(); while (enum.hasMoreElements()) { Object key = enum.nextElement(); ServerSession session = (ServerSession)sessions.get(key); long timeNow = System.currentTimeMillis(); Session sessions[] = findSessions(); session.reap(); session.validate(); } } synchronized void removeSession(ServerSession session) { String id = session.getId(); private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private static ServerSessionManager manager; // = new ServerSessionManager(); session.invalidate(); sessions.remove(id); } return (this.maxActiveSessions); } } /** * Set the maximum number of actives Sessions allowed, or -1 for * no limit. * * @param max The new maximum number of sessions */ public void setMaxActiveSessions(int max) { static { manager = new ServerSessionManager(); } public static ServerSessionManager getManager() { return manager; } private Hashtable sessions = new Hashtable(); private Reaper reaper;

private ServerSessionManager() { reaper = Reaper.getReaper(); reaper.setServerSessionManager(this); reaper.start(); } public void removeSessions(Context context) { Enume ServerSession servS=apS.getServerSession(); servS.accessed(); apS.accessed(); try { Thread.sleep(checkInterval * 1000L); } catch (InterruptedException e) { ; } } // --------------------------------------------------------- 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 getId() * method of the returned session. If a new session cannot be created * for any reason, return null. * * @exception IllegalStateException if a new session cannot be * instantiated for any reason */ public Session createSession() { /** * Start the background thread that will periodically check for * session timeouts. */ private void threadStart() { if (thread != null) return; threadDone = false; thread = new Thread(this, threadName); thread.setDaemon(true); thread.start(); if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) throw new IllegalStateException (sm.getString("standardManager.createSession.ise")); public void accessed( Context ctx, Request req, String id ) { ApplicationSession apS=(ApplicationSession)findSession( ctx, id);

if( apS==null) return; } /** * Sleep for the duration specified by the checkInterval * property. */ private void threadSleep() { this.maxActiveSessions = max; } protected int inactiveInterval = -1; for (int i = 0; i < sessions.length; i++) { StandardSession session = (StandardSession) sessions[i]; if (!session.isValid()) continue; int maxInactiveInterval = session.getMaxInactiveInterval(); if (maxInactiveInterval < 0) continue; int timeIdle = // Truncate, do not round up (int) ((timeNow - session.getLastAccessedTime()) / 1000L); if (timeIdle >= maxInactiveInterval) session.expire(); } /** * Return the maximum number of active Sessions allowed, or -1 for * no limit. */ public int getMaxActiveSessions() { } return (super.createSession()); /** * Stop the background thread that is periodically checking for * session timeouts. */ private void threadStop() { } // cache it - no need to compute it again req.setSession( apS ); } if (thread == null) return; public HttpSession createSession(Context ctx) { String sessionId = SessionIdGenerator.generateId();

ServerSession session = new ServerSession(sessionId); sessions.put(sessionId, session); threadDone = true; thread.interrupt(); try { thread.join(); } catch (InterruptedException e) { ; } if(-1 != inactiveInterval) { session.setMaxInactiveInterval(inactiveInterval); } return session.getApplicationSession( ctx, true ); } thread = null; public HttpSession findSession(Context ctx, String id) { ServerSession sSession=(ServerSession)sessions.get(id); if(sSession==null) return null; } // ------------------------------------------------------ Background Thread return sSession.getApplicationSession(ctx, false); } /** * The background thread that checks for session timeouts and shutdown. */ public void run() { // Loop until the termination semaphore is set while (!threadDone) { threadSleep(); processExpires(); } } } CASCON '04 // XXX should we throw exception or just return null ?? * IMPLEMENTATION NOTE: Once we commit to the new Manager/Session public HttpSession findSession( Context ctx, String id ) { * paradigm, I would suggest moving the logic implemented here back into * the core level. try { The Tomcat.Next "Manager" interface acts more like a

Session session = manager.findSession(id); if(session!=null) * processing semantics of handling sessions. return session.getSession(); *

} catch (IOException e) { * XXX - At present, there is no way (via the SessionManager interface) for } * a Context to tell the Manager that we create what the default session return (null); * timeout for this web application (specified in the deployment descriptor) } * should be. * public HttpSession createSession(Context ctx) { * @author Craig R. McClanahan return */ manager.createSession().getSession(); } public final class StandardSessionManager implements SessionManager { /** * Remove all sessions because our associated Context is being shut down. * * @param ctx The context that is being shut down // ----------------------------------------------------------Constructors */ public void removeSessions(Context ctx) { // XXX XXX a manager may be shared by multiple * Create a new SessionManager that adapts to the corresponding Manager // contexts, we just want to remove the sessions of ctx!

* implementation. // The manager will still run after that ( i.e. keep database */ // connection open public StandardSessionManager() { if (manager instanceof Lifecycle) { try { manager = new StandardManager(); ((Lifecycle) manager).stop(); if (manager instanceof Lifecycle) { } catch (LifecycleException e) { try { throw new IllegalStateException("" + e); ((Lifecycle) manager).configure(null); } ((Lifecycle) manager).start(); } } catch (LifecycleException e) { throw new IllegalStateException("" + e); } } } /** * Used by context to configure the session manager's inactivity timeout. } * * The SessionManager 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 conforgure the * session manager according to this value. /**

* Name to register for the background thread. */ private String threadName = "StandardManager"; } 6 req.setSession( session ); } * and lifecycle configuration is not supported. // ----------------------------------------------------- Instance Variables /** * 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 IllegalStateException if this component has not been started * @exception IllegalStateException if this component has already * been stopped * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ public void stop() throws LifecycleException { return (this.info); final class StandardSessionContext implements HttpSessionContext { ((Session) session).access(); // cache the HttpSession - avoid another find *

* XXX - At present, use of StandardManager is hard coded, // Start the background reaper thread threadStart(); } /** * The background thread completion semaphore. */ private boolean threadDone = false; // -------------------------------------------------------------- Private Class /** * This class is a dummy implementation of the HttpSessionContext * interface, to conform to the requirement that such an object be returned * when HttpSession.getSessionContext() is called. * * @author Craig R. McClanahan

* * @deprecated As of Java Servlet API 2.1 with no replacement. The * interface will be removed in a future version of this API. */ if( session == null) return; if (session instanceof Session) * Specialized implementation of org.apache.tomcat.core.SessionManager * that adapts to the new component-based Manager implementation. * collection class, and has minimal knowledge of the detailed request // Validate and update our current component state if (!configured) throw new LifecycleException (sm.getString("standardManager.notConfigured")); if (started) throw new LifecycleException (sm.getString("standardManager.alreadyStarted")); started = true; /** * Has this component been started yet? */ private boolean started = false; crosscut invalidate(StandardSession s): s & (int getMaxInactiveInterval() | long getCreationTime() | Object getAttribute(String) | Enumeration getAttributeNames() | String[] getValueNames() | void invalidate() | boolean isNew() | void removeAttribute(String) | void setAttribute(String, Object)); return (this.info); HttpSession session=findSession(ctx, id); /** /** * The string manager for this package. */ private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); } /** * Return the object bound with the specified name in this session, or * null if no object is bound with that name. * * @param name Name of the value to be returned

* * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * getAttribute() */ public Object getValue(String name) { * @param session The session to be marked */ public void accessed(Context ctx, Request req, String id) { /** node = attributes.getNamedItem("checkInterval"); if (node != null) { try { setCheckInterval(Integer.parseInt(node.getNodeValue())); } catch (Throwable t) { ; // XXX - Throw exception? } } * Prepare for the beginning of active use of the public methods of this * component. This method should be called after configure(), * and before any of the public methods of the component are utilized. * * @exception IllegalStateException if this component has not yet been * configured (if required for this component) * @exception IllegalStateException if this component has already been * started * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ public void start() throws LifecycleException { } if ((this.id != null) && (manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).remove(this); This should be * called for each request by a RequestInterceptor. * import org.apache.tomcat.core.SessionManager; import org.apache.tomcat.util.SessionUtil; } // Serialize the attribute count and the attribute values

stream.writeObject(new Integer(results.size())); Enumeration names = results.elements(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); stream.writeObject(name); stream.writeObject(attributes.get(name)); } return (attributes.keys()); * Mark the specified session's last accessed time. import org.apache.tomcat.core.Request; import org.apache.tomcat.core.Response; /** /** * The maximum number of active Sessions allowed, or -1 for no limit. */ protected int maxActiveSessions = -1; // Accumulate the names of serializable attributes Vector results = new Vector(); Enumeration attrs = getAttributeNames(); while (attrs.hasMoreElements()) { String attr = (String) attrs.nextElement(); Object value = attributes.get(attr); if (value instanceof Serializable) results.addElement(attr); } } /** * Return an Enumeration of String objects * containing the names of the objects bound to this session. * * @exception IllegalStateException if this method is called on an * invalidated session */ public Enumeration getAttributeNames() { /** import org.apache.tomcat.catalina.*; import org.apache.tomcat.core.Context; *

node = attributes.getNamedItem("maxInactiveInterval"); if (node != null) { try { setMaxInactiveInterval(Integer.parseInt(node.getNodeValue()));

} catch (Throwable t) { ; // XXX - Throw exception? } } /** * The descriptive information about this implementation. */ private static final String info = "StandardManager/1.0"; // Write the scalar instance variables (except Manager) stream.writeObject(new Long(creationTime)); stream.writeObject(id); stream.writeObject(new Long(lastAccessedTime)); stream.writeObject(new Integer(maxInactiveInterval)); stream.writeObject(new Boolean(isNew)); stream.writeObject(new Boolean(isValid)); return (attributes.get(name)); /** * Return the session identifier for this session. */ public String getId() { import javax.servlet.http.Cookie; import javax.servlet.http.HttpSession; // Parse and process our configuration parameters if (!("Manager".equals(parameters.getNodeName()))) return; NamedNodeMap attributes = parameters.getAttributes(); Node node = null; node = attributes.getNamedItem("maxActiveSessions"); if (node != null) { try { setMaxActiveSessions(Integer.parseInt(node.getNodeValue())); } catch (Throwable t) { ; // XXX - Throw exception? } } /** * Has this component been configured yet? */ private boolean configured = false; /** * Write a serialized version of this session object to the specified * object output stream. *

* IMPLEMENTATION NOTE: The owning Manager will not be stored

* in the serialized representation of this Session. After calling * readObject(), you must set the associated Manager * explicitly. *

* IMPLEMENTATION NOTE: Any attribute that is not Serializable * will be silently ignored. If you do not want any such attributes, * be sure the distributable property of our associated * Manager is set to true. * * @param stream The output stream to write to * * @exception IOException if an input/output error occurs */ private void writeObject(ObjectOutputStream stream) throws IOException { } // Validate and update our current component state if (configured) throw new LifecycleException (sm.getString("standardManager.alreadyConfigured")); configured = true; if (parameters == null) return; /** * The interval (in seconds) between checks for expired sessions. */ private int checkInterval = 60; } if (sessionContext == null) sessionContext = new StandardSessionContext(); return (sessionContext); // ----------------------------------------------------Session Properties throw new IllegalStateException(msg); } /** * 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 HttpSessionContext getSessionContext() { // --------------------------------------------------------- Public Methods

import java.io.IOException; // ----------------------------------------------------- Instance Variables // Deserialize the attribute count and attribute values int n = ((Integer) stream.readObject()).intValue(); for (int i = 0; i < n; i++) { String name = (String) stream.readObject(); Object value = (Object) stream.readObject(); attributes.put(name, value); } } ((HttpSessionBindingListener)o).valueUnbound(e); } throw new IllegalStateException(msg); } } package org.apache.tomcat.session; /** * Configure this component, based on the specified configuration * parameters. This method should be called immediately after the * component instance is created, and before start() * is called. * * @param parameters Configuration parameters for this component * (FIXME: What object type should this really be?) * * @exception IllegalStateException if this component has already been * configured and/or started * @exception LifecycleException if this component detects a fatal error * in the configuration parameters it was given */ public void configure(Node parameters) throws LifecycleException { public final class StandardManager extends ManagerBase implements Lifecycle, Runnable { return (this.creationTime); StandardSessionManager // ------------------------------------------------------ Lifecycle Methods java.io.IOException; java.util.Enumeration; java.util.Hashtable; java.util.Vector;

org.apache.tomcat.catalina.*; javax.servlet.http.Cookie; javax.servlet.http.HttpSession; org.apache.tomcat.util.StringManager; org.w3c.dom.NamedNodeMap; org.w3c.dom.Node; /** * Standard implementation of the Manager interface that provides * no session persistence or distributable capabilities, but does support * an optional, configurable, maximum number of active sessions allowed. *

* Lifecycle configuration of this component assumes an XML node * in the following format: * * <Manager className="org.apache.tomcat.session.StandardManager" * checkInterval="60" maxActiveSessions="-1" * maxInactiveInterval="-1" /> * * where you can adjust the following parameters, with default values * in square brackets: *

    *

  • checkInterval - The interval (in seconds) between background * thread checks for expired sessions. [60] *
  • maxActiveSessions - The maximum number of sessions allowed to * be active at once, or -1 for no limit. [-1] *
  • maxInactiveInterval - 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] *

* * @author Craig R. McClanahan * @version $Revision: 1.1.1.1 $ $Date: 2000/05/02 21:28:30 $ */ Hashtable valuesClone = (Hashtable)values.clone(); /** * Called by context when request comes in so that accesses and * inactivities can be dealt with accordingly. */ StandardManager package org.apache.tomcat.session;

import import import import import import import import import import } return valueNames; public Enumeration getAttributeNames() { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); synchronized (attributes) { Object object = attributes.get(name); if (object == null) return; attributes.remove(name); // System.out.println( "Removing attribute " + name ); if (object instanceof HttpSessionBindingListener) { ((HttpSessionBindingListener) object).valueUnbound (new HttpSessionBindingEvent((HttpSession) this, name)); } } // Unbind any objects associated with this session Vector results = new Vector(); Enumeration attrs = getAttributeNames(); while (attrs.hasMoreElements()) { String attr = (String) attrs.nextElement(); results.addElement(attr); } Enumeration names = results.elements(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); removeAttribute(name); } /** * Construct a new Session associated with the specified Manager. * * @param manager The manager with which this Session is associated */ public StandardSession(Manager manager) { }

} /** * 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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueUnbound() on the object. * * @param name Name of the object to remove from this session. * * @exception IllegalStateException if this method is called on an * invalidated session */ public void removeAttribute(String name) { // Remove this session from our manager's active sessions if ((manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).remove(this); names.copyInto(valueNames); this.inactiveInterval = context.getSessionTimeOut(); if (this.inactiveInterval != -1) { this.inactiveInterval *= 60; } setAttribute(name, value); } this.lastAccessedTime = this.thisAccessedTime; this.thisAccessedTime = System.currentTimeMillis(); this.isNew=false; } /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. */ public void expire() { // ---------------------------------------------------------- Constructors /** * @deprecated */ public String[] getValueNames() { Enumeration e = getAttributeNames(); Vector names = new Vector();

ApplicationSession(String id, ServerSession serverSession, Context context) { this.serverSession = serverSession; this.context = context; this.id = id; public long getLastAccessedTime() { if (valid) { return lastAccessed; } else { String msg = sm.getString("applicationSession.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() { if (name == null) { String msg = sm.getString("applicationSession.value.iae"); } * 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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueBound() 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 IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * setAttribute() */ public void putValue(String name, Object value) { // ------------------------------------------------- Session Public Methods final class StandardSession implements HttpSession, Session { } import org.apache.tomcat.core.*; import org.apache.tomcat.util.StringManager; import java.io.*;

import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /** return ((HttpSession) this); } // remove any existing binding if (value != null && value instanceof HttpSessionBindingListener) { HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, name); package org.apache.tomcat.session; /** * Return the HttpSession for which this object * is the facade. */ public HttpSession getSession() { * /** * @param minutes The session inactivity timeout in minutes. * The Manager implementation we are actually using. */ */ public void setSessionTimeOut(int minutes) { private Manager manager = null; if(-1 != minutes) { // The manager works with seconds... manager.setMaxInactiveInterval(minutes * 60); } } } problems like session tracking is not modularized HTTPRequest getCookies() getRequestURI()(doc) getSession() getRequestedSessionId()

... SessionInterceptor requestMap(request) beforeBody(req, resp) ... Session HTTPResponse getAttribute(name) setAttribute(name, val) invalidate() ... getRequest() setContentType(contentType) getOutptutStream() setSessionId(id) ... 7 Servlet CASCON '04 the problem of crosscutting concerns critical aspects of large systems dont fit in traditional modules logging, error handling synchronization security power management memory management performance optimizations tangled code has a cost difficult to understand difficult to change logging, security, optimizations 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 development and maintenance headaches 8 CASCON '04 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, lets capture the structure of crosscutting concerns explicitly... in a modular way with linguistic and tool support aspects are well-modularized crosscutting concerns Aspect-Oriented Software Development: AO support throughout lifecycle 9 CASCON '04 this tutorial is about... using AOP and AspectJ to: improve the modularity of crosscutting concerns design modularity source code modularity development process aspects are two things: concerns that crosscut [design level] a programming construct [implementation level] enables crosscutting concerns to be captured in modular units AspectJ is: an aspect-oriented extension to Java that supports general-purpose aspect-oriented programming 10 CASCON '04 language support to ApplicationSession App ca onSess on /* * ==================================================================== * * 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 [email protected] * * 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, INDIRECT, 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 * . * * [Additional notices, if required by prior licensing conditions] * */ public void invalidate() { serverSession.removeApplicationSession(context); // remove everything in the session Enumeration enum = values.keys(); while (enum.hasMoreElements()) { String name = (String)enum.nextElement(); removeValue(name); } valid = false; } public boolean isNew() { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); StandardSession S andardSess on package org.apache.tomcat.session; import import import import import import import import import import import import import import java.io.IOException; java.io.ObjectInputStream; java.io.ObjectOutputStream; java.io.Serializable; java.util.Enumeration; java.util.Hashtable; java.util.Vector; javax.servlet.ServletException; javax.servlet.http.HttpSession; javax.servlet.http.HttpSessionBindingEvent; javax.servlet.http.HttpSessionBindingListener; javax.servlet.http.HttpSessionContext; org.apache.tomcat.catalina.*;

org.apache.tomcat.util.StringManager; throw new IllegalStateException(msg); } if (thisAccessTime == creationTime) { return true; } else { return false; } } /** * @deprecated */ public void putValue(String name, Object value) { setAttribute(name, value); } public void setAttribute(String name, Object value) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); throw new IllegalStateException(msg); } if (name == null) { String msg = sm.getString("applicationSession.value.iae"); } throw new IllegalArgumentException(msg); removeValue(name); /** * Standard implementation of the Session interface. This object is * serializable, so that it can be stored in persistent storage or transferred * to a different JVM for distributable session support. *

* IMPLEMENTATION NOTE: An instance of this class represents both the * internal (Session) and application level (HttpSession) view of the session. * However, because the class itself is not declared public, Java logic outside * of the org.apache.tomcat.session package cannot cast an * HttpSession view of this instance back to a Session view. * * @author Craig R. McClanahan * @version $Revision: 1.2 $ $Date: 2000/05/15 17:54:10 $ */ ((HttpSessionBindingListener)value).valueBound(e); } values.put(name, value);

/** * @deprecated */ public Object getValue(String name) { return getAttribute(name); } public Object getAttribute(String name) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); /** * Core implementation of an application level session * * @author James Duncan Davidson [[email protected]] * @author Jason Hunter [[email protected]] * @author James Todd [[email protected]] */ throw new IllegalStateException(msg); } throw new IllegalArgumentException(msg); public class ApplicationSession implements HttpSession { return values.get(name); private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private Hashtable values = new Hashtable(); private String id; private ServerSession serverSession; private Context context; private long creationTime = System.currentTimeMillis();; private long thisAccessTime = creationTime; private long lastAccessed = creationTime; private int inactiveInterval = -1; private boolean valid = true; } while (e.hasMoreElements()) { names.addElement(e.nextElement()); } String[] valueNames = new String[names.size()]; ServerSession getServerSession() { return serverSession; throw new IllegalStateException(msg); } } } // Mark this session as invalid

setValid(false); /** * 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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueUnbound() on the object. * * @param name Name of the object to remove from this session. * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * removeAttribute() */ public void removeValue(String name) { } super(); this.manager = manager; } // ----------------------------------------------------Instance Variables /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { // Reset the instance variables associated with this Session /** * The collection of user data attributes associated with this Session. */ private Hashtable attributes = new Hashtable(); attributes.clear(); creationTime = 0L; id = null; lastAccessedTime = 0L; manager = null; maxInactiveInterval = -1; isNew = true; isValid = false; /** * The time this session was created, in milliseconds since midnight,

* January 1, 1970 GMT. */ private long creationTime = 0L; removeAttribute(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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueBound() 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 IllegalArgumentException if an attempt is made to add a * non-serializable object in an environment marked distributable. * @exception IllegalStateException if this method is called on an * invalidated session */ public void setAttribute(String name, Object value) { // ------------------------------------------------ Session Package Methods /** * Return the isValid flag for this session. */ boolean isValid() { /** * Descriptive information describing this Session implementation. */ private static final String info = "StandardSession/ 1.0"; Sess on n ercep or } // Tell our Manager that this Session has been recycled if ((manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).recycle(this); } /** * The session identifier of this Session. */ private String id = null; return (this.isValid);

/** * The last accessed time for this Session. */ private long lastAccessedTime = creationTime; if ((manager != null) && manager.getDistributable() && !(value instanceof Serializable)) throw new IllegalArgumentException (sm.getString("standardSession.setAttribute.iae")); /** * Set the isNew flag for this session. * * @param isNew The new value for the isNew flag */ void setNew(boolean isNew) { /** * The Manager with which this Session is associated. */ private Manager manager = null; synchronized (attributes) { removeAttribute(name); attributes.put(name, value); if (value instanceof HttpSessionBindingListener) ((HttpSessionBindingListener) value).valueBound (new HttpSessionBindingEvent((HttpSession) this, name)); } return (Enumeration)valuesClone.keys(); } void accessed() { // set last accessed to thisAccessTime as it will be left over // from the previous access lastAccessed = thisAccessTime; thisAccessTime = System.currentTimeMillis(); /** * @deprecated */ this.isNew = isNew; } } /** * 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 maxInactiveInterval = -1; /** * Set the isValid flag for this session. * * @param isValid The new value for the isValid flag public void removeValue(String name) { removeAttribute(name); validate(); this.isValid = isValid; /** * Flag indicating whether this session is new or } public void removeAttribute(String name) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); not. */ private boolean isNew = true; throw new IllegalStateException(msg); } if (thisInterval > inactiveInterval) { invalidate(); } /** * Flag indicating whether this session is valid or if (name == null) { String msg = sm.getString("applicationSession.value.iae"); } throw new IllegalArgumentException(msg); } not. */ private boolean isValid = false; } // HTTP SESSION IMPLEMENTATION METHODS Object o = values.get(name);

public String getId() { if (valid) { return id; } else { String msg = sm.getString("applicationSession.session.ise"); if (o instanceof HttpSessionBindingListener) { HttpSessionBindingEvent e = new HttpSessionBindingEvent(this,name); /** * Read a serialized version of this session object from the specified * object input stream. *

* IMPLEMENTATION NOTE: 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 ClassNotFoundException if an unknown class is specified * @exception IOException if an input/output error occurs */ private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException { } } void validate() { // if we have an inactive interval, check to see if we've exceeded it if (inactiveInterval != -1) { int thisInterval = (int)(System.currentTimeMillis() - lastAccessed) / 1000; // -------------------------------------------- HttpSession Private Methods */ void setValid(boolean isValid) { /** * The string manager for this package. */ private StringManager sm = // ------------------------------------------------HttpSession Properties /** * Return the time when this session was created, in milliseconds since * midnight, January 1, 1970 GMT. * * @exception IllegalStateException if this method is called on an * invalidated session */

public long getCreationTime() { // Deserialize the scalar instance variables (except Manager) creationTime = ((Long) stream.readObject()).longValue(); id = (String) stream.readObject(); lastAccessedTime = ((Long) stream.readObject()).longValue(); maxInactiveInterval = ((Integer) stream.readObject()).intValue(); isNew = ((Boolean) stream.readObject()).booleanValue(); isValid = ((Boolean) stream.readObject()).booleanValue(); StringManager.getManager("org.apache.tomcat.session"); values.remove(name); } public long getCreationTime() { if (valid) { return creationTime; } else { String msg = sm.getString("applicationSession.session.ise"); public void setMaxInactiveInterval(int interval) { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); throw new IllegalStateException(msg); } throw new IllegalStateException(msg); } inactiveInterval = interval; } } /** * * @deprecated */ public int getMaxInactiveInterval() { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); /** * The HTTP session context associated with this session. */ private static HttpSessionContext sessionContext = null; /** * The current accessed time for this session. */ private long thisAccessedTime = creationTime;

public HttpSessionContext getSessionContext() { return new SessionContextImpl(); } return inactiveInterval; } } //----------------------------------------------------------------------- throw new IllegalStateException(msg); // ----------------------------------------------HttpSession Public Methods /** * 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 setCreationTime(long time) { } this.creationTime = time; this.lastAccessedTime = time; this.thisAccessedTime = time; } /** * Return the object bound with the specified name in this session, or * null if no object is bound with that name. * * @param name Name of the attribute to be returned * * @exception IllegalStateException if this method is called on an * invalidated session */ public Object getAttribute(String name) { } return (this.id); } /** * Set the session identifier for this session. * * @param id The new session identifier */

public void setId(String id) { this.id = id; ServerSess on if ((manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).add(this); } /** * Return descriptive information about this Session implementation and * the corresponding version number, in the format * <description>/<version>. */ public String getInfo() { static advice(StandardSession s): invalidate(s) { before { if (!s.isValid()) throw new IllegalStateException (s.sm.getString("standardSession." + thisJoinPoint.methodName + ".ise")); } } return (getAttribute(name)); } } /** * 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 getLastAccessedTime() { /** * Return the set of names of objects bound to this session. If there * are no such objects, a zero-length array is returned. * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * getAttributeNames()

*/ public String[] getValueNames() { Vector results = new Vector(); Enumeration attrs = getAttributeNames(); while (attrs.hasMoreElements()) { String attr = (String) attrs.nextElement(); results.addElement(attr); } String names[] = new String[results.size()]; for (int i = 0; i < names.length; i++) names[i] = (String) results.elementAt(i); return (names); return (this.lastAccessedTime); } /** * Return the Manager within which this Session is valid. */ public Manager getManager() { } /** * The background thread. */ private Thread thread = null; 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 Enumeration * and will be removed in a future version of the API. */ public Enumeration getIds() { } return (this.manager); } /** * Invalidates this session and unbinds any objects bound to it. /** * Set the Manager within which this Session is valid. * * @param manager The new Manager */ public void setManager(Manager manager) {

* * @exception IllegalStateException if this method is called on * an invalidated session */ public void invalidate() { return (dummy.elements()); } // Cause this session to expire expire(); this.manager = manager; } /** * Return the HttpSession 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 HttpSession getSession(String id) { } /** * 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 IllegalStateException if this method is called on * an invalidated session */ public int getMaxInactiveInterval() { return (this.maxInactiveInterval); /** * Return true 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 IllegalStateException if this method is called on an * invalidated session */ public boolean isNew() { } return (this.isNew); /** * 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 setMaxInactiveInterval(int interval) { this.maxInactiveInterval = interval; } return (null); } // ------------------------------------------------------------- Properties // Validate and update our current component state if (!started) throw new LifecycleException (sm.getString("standardManager.notStarted")); started = false; /** * Return the check interval (in seconds) for this Manager. */ public int getCheckInterval() { return (this.checkInterval); // Stop the background reaper thread threadStop(); } // Expire all active sessions Session sessions[] = findSessions(); for (int i = 0; i < sessions.length; i++) { StandardSession session = (StandardSession) sessions[i]; if (!session.isValid()) continue; session.expire(); } /**

* Set the check interval (in seconds) for this Manager. * * @param checkInterval The new check interval */ public void setCheckInterval(int checkInterval) { ServerSessionManager ServerSess onManager this.checkInterval = checkInterval; } } // -------------------------------------------------------- Private Methods 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.*; /** * * @author James Duncan Davidson [[email protected]] * @author Jason Hunter [[email protected]] * @author James Todd [[email protected]] */ public class ServerSessionManager implements SessionManager { // 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. /** * Return descriptive information about this Manager implementation and * the corresponding version number, in the format * <description>/<version>. */ public String getInfo() { /** * Invalidate all sessions that have expired. */ private void processExpires() { synchronized void reap() { Enumeration enum = sessions.keys(); while (enum.hasMoreElements()) { Object key = enum.nextElement(); ServerSession session = (ServerSession)sessions.get(key);

long timeNow = System.currentTimeMillis(); Session sessions[] = findSessions(); session.reap(); session.validate(); } } synchronized void removeSession(ServerSession session) { String id = session.getId(); private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); private static ServerSessionManager manager; // = new ServerSessionManager(); session.invalidate(); sessions.remove(id); } return (this.maxActiveSessions); } } /** * Set the maximum number of actives Sessions allowed, or -1 for * no limit. * * @param max The new maximum number of sessions */ public void setMaxActiveSessions(int max) { static { manager = new ServerSessionManager(); } public static ServerSessionManager getManager() { return manager; } private Hashtable sessions = new Hashtable(); private Reaper reaper; private ServerSessionManager() { reaper = Reaper.getReaper(); reaper.setServerSessionManager(this); reaper.start(); } public void removeSessions(Context context) { Enume ServerSession servS=apS.getServerSession(); servS.accessed(); apS.accessed(); try { Thread.sleep(checkInterval * 1000L); } catch (InterruptedException e) { ;

} } // --------------------------------------------------------- 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 getId() * method of the returned session. If a new session cannot be created * for any reason, return null. * * @exception IllegalStateException if a new session cannot be * instantiated for any reason */ public Session createSession() { /** * Start the background thread that will periodically check for * session timeouts. */ private void threadStart() { if (thread != null) return; threadDone = false; thread = new Thread(this, threadName); thread.setDaemon(true); thread.start(); if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) throw new IllegalStateException (sm.getString("standardManager.createSession.ise")); public void accessed( Context ctx, Request req, String id ) { ApplicationSession apS=(ApplicationSession)findSession( ctx, id); if( apS==null) return; } /** * Sleep for the duration specified by the checkInterval * property. */ private void threadSleep() { this.maxActiveSessions = max; } protected int inactiveInterval = -1; for (int i = 0; i < sessions.length; i++) { StandardSession session = (StandardSession) sessions[i];

if (!session.isValid()) continue; int maxInactiveInterval = session.getMaxInactiveInterval(); if (maxInactiveInterval < 0) continue; int timeIdle = // Truncate, do not round up (int) ((timeNow - session.getLastAccessedTime()) / 1000L); if (timeIdle >= maxInactiveInterval) session.expire(); } /** * Return the maximum number of active Sessions allowed, or -1 for * no limit. */ public int getMaxActiveSessions() { } return (super.createSession()); /** * Stop the background thread that is periodically checking for * session timeouts. */ private void threadStop() { } // cache it - no need to compute it again req.setSession( apS ); } if (thread == null) return; public HttpSession createSession(Context ctx) { String sessionId = SessionIdGenerator.generateId(); ServerSession session = new ServerSession(sessionId); sessions.put(sessionId, session); threadDone = true; thread.interrupt(); try { thread.join(); } catch (InterruptedException e) { ; } if(-1 != inactiveInterval) { session.setMaxInactiveInterval(inactiveInterval); } return session.getApplicationSession( ctx, true ); }

thread = null; public HttpSession findSession(Context ctx, String id) { ServerSession sSession=(ServerSession)sessions.get(id); if(sSession==null) return null; } // ------------------------------------------------------ Background Thread return sSession.getApplicationSession(ctx, false); } /** * The background thread that checks for session timeouts and shutdown. */ public void run() { // Loop until the termination semaphore is set while (!threadDone) { threadSleep(); processExpires(); } } } CASCON '04 // XXX should we throw exception or just return null ?? * IMPLEMENTATION NOTE: Once we commit to the new Manager/Session public HttpSession findSession( Context ctx, String id ) { * paradigm, I would suggest moving the logic implemented here back into * the core level. try { The Tomcat.Next "Manager" interface acts more like a Session session = manager.findSession(id); if(session!=null) * processing semantics of handling sessions. return session.getSession(); *

} catch (IOException e) { * XXX - At present, there is no way (via the SessionManager interface) for } * a Context to tell the Manager that we create what the default session

return (null); * timeout for this web application (specified in the deployment descriptor) } * should be. * public HttpSession createSession(Context ctx) { * @author Craig R. McClanahan return */ manager.createSession().getSession(); } public final class StandardSessionManager implements SessionManager { /** * Remove all sessions because our associated Context is being shut down. * * @param ctx The context that is being shut down // ----------------------------------------------------------Constructors */ public void removeSessions(Context ctx) { // XXX XXX a manager may be shared by multiple * Create a new SessionManager that adapts to the corresponding Manager // contexts, we just want to remove the sessions of ctx! * implementation. // The manager will still run after that ( i.e. keep database */ // connection open public StandardSessionManager() { if (manager instanceof Lifecycle) { try { manager = new StandardManager(); ((Lifecycle) manager).stop();

if (manager instanceof Lifecycle) { } catch (LifecycleException e) { try { throw new IllegalStateException("" + e); ((Lifecycle) manager).configure(null); } ((Lifecycle) manager).start(); } } catch (LifecycleException e) { throw new IllegalStateException("" + e); } } } /** * Used by context to configure the session manager's inactivity timeout. } * * The SessionManager 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 conforgure the * session manager according to this value. /** * Name to register for the background thread. */ private String threadName = "StandardManager"; } 11 req.setSession( session ); } * and lifecycle configuration is not supported. // ----------------------------------------------------- Instance Variables /**

* 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 IllegalStateException if this component has not been started * @exception IllegalStateException if this component has already * been stopped * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ public void stop() throws LifecycleException { return (this.info); final class StandardSessionContext implements HttpSessionContext { ((Session) session).access(); // cache the HttpSession - avoid another find *

* XXX - At present, use of StandardManager is hard coded, // Start the background reaper thread threadStart(); } /** * The background thread completion semaphore. */ private boolean threadDone = false; // -------------------------------------------------------------- Private Class /** * This class is a dummy implementation of the HttpSessionContext * interface, to conform to the requirement that such an object be returned * when HttpSession.getSessionContext() is called. * * @author Craig R. McClanahan * * @deprecated As of Java Servlet API 2.1 with no replacement. The * interface will be removed in a future version of this API. */ if( session == null) return; if (session instanceof Session) * Specialized implementation of org.apache.tomcat.core.SessionManager * that adapts to the new component-based Manager implementation. * collection class, and has minimal knowledge of the detailed request // Validate and update our current component state if (!configured) throw new LifecycleException (sm.getString("standardManager.notConfigured")); if (started)

throw new LifecycleException (sm.getString("standardManager.alreadyStarted")); started = true; /** * Has this component been started yet? */ private boolean started = false; crosscut invalidate(StandardSession s): s & (int getMaxInactiveInterval() | long getCreationTime() | Object getAttribute(String) | Enumeration getAttributeNames() | String[] getValueNames() | void invalidate() | boolean isNew() | void removeAttribute(String) | void setAttribute(String, Object)); return (this.info); HttpSession session=findSession(ctx, id); /** /** * The string manager for this package. */ private StringManager sm = StringManager.getManager("org.apache.tomcat.session"); } /** * Return the object bound with the specified name in this session, or * null if no object is bound with that name. * * @param name Name of the value to be returned * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * getAttribute() */ public Object getValue(String name) { * @param session The session to be marked */ public void accessed(Context ctx, Request req, String id) { /** node = attributes.getNamedItem("checkInterval");

if (node != null) { try { setCheckInterval(Integer.parseInt(node.getNodeValue())); } catch (Throwable t) { ; // XXX - Throw exception? } } * Prepare for the beginning of active use of the public methods of this * component. This method should be called after configure(), * and before any of the public methods of the component are utilized. * * @exception IllegalStateException if this component has not yet been * configured (if required for this component) * @exception IllegalStateException if this component has already been * started * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ public void start() throws LifecycleException { } if ((this.id != null) && (manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).remove(this); This should be * called for each request by a RequestInterceptor. * import org.apache.tomcat.core.SessionManager; import org.apache.tomcat.util.SessionUtil; } // Serialize the attribute count and the attribute values stream.writeObject(new Integer(results.size())); Enumeration names = results.elements(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); stream.writeObject(name); stream.writeObject(attributes.get(name)); } return (attributes.keys()); * Mark the specified session's last accessed time. import org.apache.tomcat.core.Request; import org.apache.tomcat.core.Response; /**

/** * The maximum number of active Sessions allowed, or -1 for no limit. */ protected int maxActiveSessions = -1; // Accumulate the names of serializable attributes Vector results = new Vector(); Enumeration attrs = getAttributeNames(); while (attrs.hasMoreElements()) { String attr = (String) attrs.nextElement(); Object value = attributes.get(attr); if (value instanceof Serializable) results.addElement(attr); } } /** * Return an Enumeration of String objects * containing the names of the objects bound to this session. * * @exception IllegalStateException if this method is called on an * invalidated session */ public Enumeration getAttributeNames() { /** import org.apache.tomcat.catalina.*; import org.apache.tomcat.core.Context; *

node = attributes.getNamedItem("maxInactiveInterval"); if (node != null) { try { setMaxInactiveInterval(Integer.parseInt(node.getNodeValue())); } catch (Throwable t) { ; // XXX - Throw exception? } } /** * The descriptive information about this implementation. */ private static final String info = "StandardManager/1.0"; // Write the scalar instance variables (except Manager) stream.writeObject(new Long(creationTime)); stream.writeObject(id); stream.writeObject(new Long(lastAccessedTime)); stream.writeObject(new Integer(maxInactiveInterval)); stream.writeObject(new Boolean(isNew));

stream.writeObject(new Boolean(isValid)); return (attributes.get(name)); /** * Return the session identifier for this session. */ public String getId() { import javax.servlet.http.Cookie; import javax.servlet.http.HttpSession; // Parse and process our configuration parameters if (!("Manager".equals(parameters.getNodeName()))) return; NamedNodeMap attributes = parameters.getAttributes(); Node node = null; node = attributes.getNamedItem("maxActiveSessions"); if (node != null) { try { setMaxActiveSessions(Integer.parseInt(node.getNodeValue())); } catch (Throwable t) { ; // XXX - Throw exception? } } /** * Has this component been configured yet? */ private boolean configured = false; /** * Write a serialized version of this session object to the specified * object output stream. *

* IMPLEMENTATION NOTE: The owning Manager will not be stored * in the serialized representation of this Session. After calling * readObject(), you must set the associated Manager * explicitly. *

* IMPLEMENTATION NOTE: Any attribute that is not Serializable * will be silently ignored. If you do not want any such attributes, * be sure the distributable property of our associated * Manager is set to true. * * @param stream The output stream to write to * * @exception IOException if an input/output error occurs */ private void writeObject(ObjectOutputStream stream) throws IOException { }

// Validate and update our current component state if (configured) throw new LifecycleException (sm.getString("standardManager.alreadyConfigured")); configured = true; if (parameters == null) return; /** * The interval (in seconds) between checks for expired sessions. */ private int checkInterval = 60; } if (sessionContext == null) sessionContext = new StandardSessionContext(); return (sessionContext); // ----------------------------------------------------Session Properties throw new IllegalStateException(msg); } /** * 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 HttpSessionContext getSessionContext() { // --------------------------------------------------------- Public Methods import java.io.IOException; // ----------------------------------------------------- Instance Variables // Deserialize the attribute count and attribute values int n = ((Integer) stream.readObject()).intValue(); for (int i = 0; i < n; i++) { String name = (String) stream.readObject(); Object value = (Object) stream.readObject(); attributes.put(name, value); } } ((HttpSessionBindingListener)o).valueUnbound(e); }

throw new IllegalStateException(msg); } } package org.apache.tomcat.session; /** * Configure this component, based on the specified configuration * parameters. This method should be called immediately after the * component instance is created, and before start() * is called. * * @param parameters Configuration parameters for this component * (FIXME: What object type should this really be?) * * @exception IllegalStateException if this component has already been * configured and/or started * @exception LifecycleException if this component detects a fatal error * in the configuration parameters it was given */ public void configure(Node parameters) throws LifecycleException { public final class StandardManager extends ManagerBase implements Lifecycle, Runnable { return (this.creationTime); StandardSessionManager S andardSess onManager // ------------------------------------------------------ Lifecycle Methods java.io.IOException; java.util.Enumeration; java.util.Hashtable; java.util.Vector; org.apache.tomcat.catalina.*; javax.servlet.http.Cookie; javax.servlet.http.HttpSession; org.apache.tomcat.util.StringManager; org.w3c.dom.NamedNodeMap; org.w3c.dom.Node; /** * Standard implementation of the Manager interface that provides * no session persistence or distributable capabilities, but does support * an optional, configurable, maximum number of active sessions allowed. *

* Lifecycle configuration of this component assumes an XML node * in the following format: *

* <Manager className="org.apache.tomcat.session.StandardManager" * checkInterval="60" maxActiveSessions="-1" * maxInactiveInterval="-1" /> * * where you can adjust the following parameters, with default values * in square brackets: *

    *

  • checkInterval - The interval (in seconds) between background * thread checks for expired sessions. [60] *
  • maxActiveSessions - The maximum number of sessions allowed to * be active at once, or -1 for no limit. [-1] *
  • maxInactiveInterval - 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] *

* * @author Craig R. McClanahan * @version $Revision: 1.1.1.1 $ $Date: 2000/05/02 21:28:30 $ */ Hashtable valuesClone = (Hashtable)values.clone(); /** * Called by context when request comes in so that accesses and * inactivities can be dealt with accordingly. */ StandardManager S andardManager package org.apache.tomcat.session; import import import import import import import import import import }

return valueNames; public Enumeration getAttributeNames() { if (! valid) { String msg = sm.getString("applicationSession.session.ise"); synchronized (attributes) { Object object = attributes.get(name); if (object == null) return; attributes.remove(name); // System.out.println( "Removing attribute " + name ); if (object instanceof HttpSessionBindingListener) { ((HttpSessionBindingListener) object).valueUnbound (new HttpSessionBindingEvent((HttpSession) this, name)); } } // Unbind any objects associated with this session Vector results = new Vector(); Enumeration attrs = getAttributeNames(); while (attrs.hasMoreElements()) { String attr = (String) attrs.nextElement(); results.addElement(attr); } Enumeration names = results.elements(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); removeAttribute(name); } /** * Construct a new Session associated with the specified Manager. * * @param manager The manager with which this Session is associated */ public StandardSession(Manager manager) { } } /** * 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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueUnbound() on the object. * * @param name Name of the object to remove from this session.

* * @exception IllegalStateException if this method is called on an * invalidated session */ public void removeAttribute(String name) { // Remove this session from our manager's active sessions if ((manager != null) && (manager instanceof ManagerBase)) ((ManagerBase) manager).remove(this); names.copyInto(valueNames); this.inactiveInterval = context.getSessionTimeOut(); if (this.inactiveInterval != -1) { this.inactiveInterval *= 60; } setAttribute(name, value); } this.lastAccessedTime = this.thisAccessedTime; this.thisAccessedTime = System.currentTimeMillis(); this.isNew=false; } /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. */ public void expire() { // ---------------------------------------------------------- Constructors /** * @deprecated */ public String[] getValueNames() { Enumeration e = getAttributeNames(); Vector names = new Vector(); ApplicationSession(String id, ServerSession serverSession, Context context) { this.serverSession = serverSession; this.context = context; this.id = id; public long getLastAccessedTime() { if (valid) { return lastAccessed; } else { String msg = sm.getString("applicationSession.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() { if (name == null) { String msg = sm.getString("applicationSession.value.iae"); } * 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. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueBound() 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 IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * setAttribute() */ public void putValue(String name, Object value) { // ------------------------------------------------- Session Public Methods final class StandardSession implements HttpSession, Session { } import org.apache.tomcat.core.*; import org.apache.tomcat.util.StringManager; import java.io.*; import java.net.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; /** return ((HttpSession) this); } // remove any existing binding if (value != null && value instanceof HttpSessionBindingListener) {

HttpSessionBindingEvent e = new HttpSessionBindingEvent(this, name); package org.apache.tomcat.session; /** * Return the HttpSession for which this object * is the facade. */ public HttpSession getSession() { * /** * @param minutes The session inactivity timeout in minutes. * The Manager implementation we are actually using. */ */ public void setSessionTimeOut(int minutes) { private Manager manager = null; if(-1 != minutes) { // The manager works with seconds... manager.setMaxInactiveInterval(minutes * 60); } } } AspectJ is a small and well-integrated extension to Java outputs .class files compatible with any JVM all Java programs are AspectJ programs a general-purpose AO language just as Java is a general-purpose OO language includes IDE support emacs, JBuilder, Forte 4J, Eclipse freely available implementation compiler is Open Source active user community [email protected] 12 CASCON '04 AspectJ applied to a large middleware system

java code base with 10,000 files and 500 developers AspectJ 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 AspectJ 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 dont 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 AspectJ to capture that AspectJ language language mechanisms: crosscutting in the code mechanisms AspectJ provides 14 CASCON '04 Part II tutorial 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 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 * FigureElement Figure makePoint(..) makeLine(..) Point 18 getX() getY() setX(int) setY(int) moveBy(int, int) CASCON '04 moveBy(int, int) 2 Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) operations that move elements a simple figure editor class Line implements FigureElement{ private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { ... } }

class Point implements FigureElement { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(int dx, int dy) { ... } } 19 CASCON '04 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.moveBy(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 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.moveBy(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 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.setP1(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.setP1(Point) call or call(void Line.setP1(Point)) || call(void Line.setP2(Point)); a void Line.setP2(Point) call whenever a Line receives a void setP1(Point) or void setP2(Point) method call 25 CASCON '04 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.setP1(Point)) || call(void Line.setP2(Point)); more on parameters and how pointcut can expose values at join points in a few slides 26 CASCON '04 pointcuts user-defined pointcut pointcut move():

call(void Line.setP1(Point)) || call(void Line.setP2(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 the way back out pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)); after() returning: move() { } 28 CASCON '04 a Line a simple aspect DisplayUpdating v1 an aspect defines a special class that can crosscut other classes aspect DisplayUpdating { pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)); after() returning: move() { Display.update(); } } 29 box means complete running code CASCON '04

without AspectJ DisplayUpdating v1 class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; Display.update(); } void setP2(Point p2) { this.p2 = p2; 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.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)); 31 CASCON '04 pointcuts can use interface signatures pointcut move(): call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int));

32 CASCON '04 a multi-class aspect DisplayUpdating v2 aspect DisplayUpdating { pointcut move(): call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)); after() returning: move() { Display.update(); } 33 } CASCON '04 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(FigureElement figElt): target(figElt) && (call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); 34 after(FigureElement fe) returning: move(fe) { } CASCON '04 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 pointcut parameters pointcut move(Line l): target(l) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point))); typed variable in place of type name 35 after(Line line) returning: move(line) { } CASCON '04 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.setP1(Point)) || call(void Line.setP2(Point))); advice parameters 36 typed variable in place of type name after(Line line) returning: move(line) { } CASCON '04 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.setP1(Point)) || call(void Line.setP2(Point))); 37 after(Line line) returning: move(line) { } CASCON '04 target primitive pointcut designator target( TypeName | FormalReference ) 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(FigureElement) 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( SupertypeName ) && does not further restrict the join points does pick up the target object pointcut move(FigureElement figElt): target(figElt) && (call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); after(FigureElement fe) returning: move(fe) { } 39

CASCON '04 pointcuts can expose values at join points pointcut move(FigureElement figElt): target(figElt) && (call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); 40 CASCON '04 context & multiple classes DisplayUpdating v3 aspect DisplayUpdating { pointcut move(FigureElement figElt): target(figElt) && (call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); after(FigureElement fe) returning: move(fe) { Display.update(fe); } } 41 CASCON '04 without AspectJ class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; }

void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { } } class Point { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(int dx, int dy) { } 42 } CASCON '04 without AspectJ class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; Display.update(); } void setP2(Point p2) { this.p2 = p2; Display.update(); } void moveBy(int dx, int dy) { } } class Point { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; }

void setY(int y) { this.y = y; } void moveBy(int dx, int dy) { } 43 } CASCON '04 DisplayUpdating v1 without AspectJ class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; Display.update(); } void setP2(Point p2) { this.p2 = p2; Display.update(); } void moveBy(int dx, int dy) { } } class Point { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; Display.update(); } void setY(int y) { this.y = y; Display.update(); } void moveBy(int dx, int dy) { } 44 } CASCON '04 DisplayUpdating v2 without AspectJ

DisplayUpdating v3 class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; Display.update(this); } void setP2(Point p2) { this.p2 = p2; Display.update(this); } void moveBy(int dx, int dy) { } } class Point { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; Display.update(this); } void setY(int y) { this.y = y; Display.update(this); } void moveBy(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 AspectJ class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1;

} void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { } } class Point { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(int dx, int dy) { } 46 } CASCON '04 with AspectJ DisplayUpdating v1 class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } aspect DisplayUpdating { pointcut move(): call(void Line.setP1(Point)) || call(void Line.setP2(Point)); void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { } } class Point {

private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(int dx, int dy) { } 47 } CASCON '04 after() returning: move() { Display.update(); } } with AspectJ DisplayUpdating v2 class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } aspect DisplayUpdating { pointcut move(): call(void FigureElement.moveBy(int, int) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)); void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { } } class Point {

private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(int dx, int dy) { } 48 } CASCON '04 after() returning: move() { Display.update(); } } with AspectJ DisplayUpdating v3 class Line { private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } aspect DisplayUpdating { pointcut move(FigureElement figElt): target(figElt) && (call(void FigureElement.moveBy(int, int) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int))); void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { } after(FigureElement fe) returning: move(fe) { Display.update(fe); }

} class Point } { private int x = 0, y = 0; int getX() { return x; } int getY() { return y; } void setX(int x) { this.x = x; } void setY(int y) { this.y = y; } void moveBy(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 * FigureElement Figure makePoint(..) makeLine(..) Point 50 getX() getY() setX(int) setY(int) moveBy(int, int) CASCON '04 moveBy(int, int) 2

Line getP1() getP2() setP1(Point) setP2(Point) moveBy(int, int) DisplayUpdating 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 post-conditions check whether values were set condition enforcement force parameters to be valid 52 CASCON '04 pre-condition using before advice aspect PointBoundsPreCondition { before(int newX): call(void Point.setX(int)) && args(newX) { assert newX >= MIN_X; what follows the : is assert newX <= MAX_X;

always a pointcut } primitive or user-defined before(int newY): call(void Point.setY(int)) && args(newY) { assert newY >= MIN_Y; assert newY <= MAX_Y; } } 53 CASCON '04 post-condition using after advice aspect PointBoundsPostCondition { after(Point p, int newX) returning: call(void Point.setX(int)) && target(p) && args(newX) { assert p.getX() == newX; } after(Point p, int newY) returning: call(void Point.setY(int)) && target(p) && args(newY) { assert p.getY() == newY; } } 54 CASCON '04 condition enforcement using around advice aspect PointBoundsEnforcement { void around(int newX): call(void Point.setX(int)) && args(newX) { proceed( clip(newX, MIN_X, MAX_X) ); } void around(int newY): call(void Point.setY(int)) && args(newY) { proceed( clip(newY, 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 ReturnType around(T1 arg1, T2 arg2, ) there is a special method with the signature ReturnType proceed(T1, T2, ) 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 PointCaching { private MyLookupTable cache = new MyLookupTable(); 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 C2 { package com.parc.print; package com.parc.copy; public int frotz() { public class C1 { public class C3 { A.doSomething(); public void foo() { public String s1() { } A.doSomething(); A.doSomething();

public int bar() { A.doSomething(); } } } } } } crosscuts of methods with a common property public/private, return a certain value, in a particular package logging, debugging, profiling log on entry to every public method 58 CASCON '04 property-based crosscutting aspect PublicErrorLogging { Logger log = Logger.global; neatly captures public interface of my packages pointcut publicInterface(): call(public * com.bigboxco..*.*(..)); after() throwing (Error e): publicInterface() { 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 whats really going on is explicit 59 CASCON '04 wildcarding in pointcuts target(Point) target(graphics.geom.Point) target(graphics.geom.*)

target(graphics..*) any type in graphics.geom any type in any sub-package of graphics call(void Point.setX(int)) call(public * Point.*(..)) call(public * *(..)) any public method on Point any public method on any type call(void call(void call(void call(void any setter Point.setX(int)) Point.setY(*)) Point.set*(*)) set*(*)) call(Point.new(int, int)) call(new(..)) 60 * is wild card .. is multi-part wild card CASCON '04 any constructor special value thisJoinPoint. Signature Object[] ... reflective* access to the join point getSignature() getArgs() available in any advice (also thisJoinPointStaticPart with only the statically determinable portions) * introspective subset of reflection consistent with Java 61 CASCON '04

using thisJoinPoint in highly polymorphic advice aspect PublicErrorLogging { Logger log = Logger.global; pointcut publicInterface(): call(public * com.bigboxco..*.*(..)); after() throwing (Error e): publicInterface() { log.throwing( tjp.getSignature().getDeclaringType().getName(), tjp.getSignature().getName(), e); } } please read as thisJoinPoint 62 CASCON '04 using thisJoinPoint makes it possible for the advice to recover information about where it is running other primitive pointcuts this( TypeName ) within( TypeName ) withincode( MemberSignature ) 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 makeLine(Line p1, Line p2) { new Line... } public Point makePoint(int x, int y) { new Point... } ... } want to ensure that any creation of

figure elements goes through the factory methods aspect FactoryEnforcement { pointcut illegalNewFigElt(): (call(Point.new(..)) || call(Line.new(..))) && !withincode(* Figure.make*(..)); before(): illegalNewFigElt() { throw new Error("Use factory method instead."); } 64 } CASCON '04 fine-grained protection a compile-time error class Figure { public Line makeLine(Line p1, Line p2) { new Line... } public Point makePoint(int x, int y) { new Point... } ... } want to ensure that any creation of figure elements goes through the factory methods aspect FactoryEnforcement { pointcut illegalNewFigElt(): (call(Point.new(..)) || call(Line.new(..))) && !withincode(* Figure.make*(..)); declare error: illegalNewFigElt(): "Use factory method instead."; } 65 } CASCON '04 must be a static pointcut fine-grained protection a compile-time error class Figure { public Line makeLine(Line p1, Line p2) { new Line... } public Point makePoint(int x, int y) { new Point... } ... }

want to ensure that any creation of figure elements goes through the factory methods aspect FactoryEnforcement { pointcut illegalNewFigElt(): call(FigureElement+.new(..)) && !withincode(* Figure.make*(..)); declare error: illegalNewFigElt(): "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 FigureElement{ private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { ... } static aspect SetterEnforcement { declare error: set(Point Line.*) && !withincode(void Line.setP*(Point)) "Use setter method."; } } 67 CASCON '04 fine-grained protection as a static inner aspect class Line implements FigureElement{ private Point p1, p2; Point getP1() { return p1; } Point getP2() { return p2; } void setP1(Point p1) { this.p1 = p1; } void setP2(Point p2) { this.p2 = p2; } void moveBy(int dx, int dy) { ... } static aspect SetterEnforcement { declare error: set(Point Line.*) &&

!withincode(void Line.setP*(Point)) "Use setter method, even inside Line class."; } } 68 CASCON '04 other primitive pointcuts execution(void Point.setX(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 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 DisplayUpdating v4 aspect DisplayUpdating { pointcut move(FigureElement fe): target(fe) && (call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) || call(void Point.setX(int)) || call(void Point.setY(int)));

pointcut topLevelMove(FigureElement fe): move(fe) && !cflowbelow(move(FigureElement)); after(FigureElement fe) returning: topLevelMove(fe) { Display.update(fe); } } 71 CASCON '04 inter-type declarations like member declarations... long void 72 CASCON '04 l = 37; m() { ... } inter-type declarations like member declarations, but with a TargetType long TargetType.l = 37; void TargetType.m() { ... } 73 CASCON '04 one display per figure element DisplayUpdating v5 aspect DisplayUpdating { private Display FigureElement.display; static void setDisplay(FigureElement fe, Display d) { fe.display = d; } pointcut move(FigureElement figElt): ; after(FigureElement fe): move(fe) { fe.display.update(fe); } } 74 CASCON '04 field/getter/setter idiom aspect DisplayUpdating {

private with respect to enclosing aspect declaration private Display FigureElement.display; public static void setDisplay(FigureElement fe, Display d) { fe.display = d; } the display field pointcut move(FigureElement figElt): ; is a field in objects of type FigureElement, but belongs to DisplayUpdating aspect after(FigureElement fe): move(fe) should { DisplayUpdating provide getter/setter fe.display.update(fe); (called by setup code) } } 75 CASCON '04 one-to-many DisplayUpdating v6 aspect DisplayUpdating { private List FigureElement.displays = new LinkedList(); public static void addDisplay(FigureElement fe, Display d) { fe.displays.add(d); } public static void removeDisplay(FigureElement fe, Display d) { fe.displays.remove(d); } pointcut move(FigureElement figElt): ; after(FigureElement fe): move(fe) { Iterator iter = fe.displays.iterator(); ... } 76 } CASCON '04 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 { } protected interface Observer { } private List Subject.observers = new ArrayList(); public void addObserver(Subject s, Observer o) { ... } public void removeObserver(Subject s, Observer o) { ... } public static List getObservers(Subject s) { ... } abstract pointcut changes(Subject s); after(Subject s): changes(s) { Iterator iter = getObservers(s).iterator(); while ( iter.hasNext() ) { notifyObserver(s, ((Observer)iter.next())); } } abstract void notifyObserver(Subject s, Observer o); 78 } CASCON '04 this is the concrete reuse DisplayUpdating v7 aspect DisplayUpdating extends Observing { declare parents: FigureElement implements Subject; declare parents: Display implements Observer; pointcut changes(Subject s): target(s) && (call(void FigureElement.moveBy(int, int)) || call(void Line.setP1(Point)) || call(void Line.setP2(Point)) ||

call(void Point.setX(int)) || call(void Point.setY(int))); void notifyObserver(Subject s, Observer o) { ((Display)o).update(s); } } 79 CASCON '04 advice precedence what happens if two pieces of advice apply to the same join point? aspect Security { before(): call(public *(..)) { if (!Policy.isAllwed(tjp)) throw new SecurityExn(); } please read as thisJoinPoint 80 CASCON '04 } aspect Logging { before(): logged() { System.err.println( "Entering " + tjp); } pointcut logged(): call(void troublesomeMethod()); } advice precedence order is undefined, unless... in the same aspect, in subaspect, or aspect Security { before(): call(public *(..)) using declare if (!Policy.isAllwed(tjp)) throw new SecurityExn(); precedence... { } declare precedence: Security, *; } aspect Logging { before(): logged() {

System.err.println( "Entering " + tjp); } pointcut logged(): call(void troublesomeMethod()); } 81 CASCON '04 summary join points method & constructor call execution field get set exception handler execution initialization aspects crosscutting type 82 CASCON '04 pointcuts -primitivecall execution handler get set initialization dispatch this target args within withincode cflow cflowbelow -user-definedpointcut declaration abstract overriding advice before after around inter-type decls Type.field Type.method() declare error

parents precedence reflection thisJoinPoint thisJoinPointStaticPart where we have been and where we are going problem structure examples: crosscutting in the design, and how to use AspectJ to capture that AspectJ language language mechanisms: crosscutting in the code mechanisms AspectJ provides 83 CASCON '04 using aspects present examples of aspects in design intuitions for identifying aspects present implementations in AspectJ how the language support can help putting AspectJ 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 very simple advice an unpluggable aspect core program functionality is unaffected by the aspect 85 CASCON '04

tracing without AspectJ class TraceSupport { TraceSupport static int TRACELEVEL = 0; static protected PrintStream stream = null; static protected int callDepth = -1; static void init(PrintStream _s) {stream=_s;} static void traceEntry(String str) { if (TRACELEVEL == 0) return; callDepth++; printEntering(str); } static void traceExit(String str) { if (TRACELEVEL == 0) return; callDepth--; printExiting(str); } 86 class Point { } void set(int x, int y) { TraceSupport.traceEntry("Point.set"); this.x = x; this.y = y; TraceSupport.traceExit("Point.set"); } } CASCON '04 a clear crosscutting structure TraceSupport 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 aspect PointTracing { TraceSupport pointcut trace(): within(com.bigboxco.boxes.*) && execution(* *(..));

before(): trace() { TraceSupport.traceEntry(tjp); } after(): trace() { TraceSupport.traceExit(tjp); } } 88 CASCON '04 plug and debug plug in: unplug: or 89 CASCON '04 ajc Point.java Line.java TraceSupport.java PointTracing.java ajc Point.java Line.java plug and debug //From ContextManager public void service( Request rrequest, Response rresponse ) { // log( "New request " + rrequest ); try { // System.out.print("A"); rrequest.setContextManager( this ); rrequest.setResponse(rresponse); rresponse.setRequest(rrequest); // wront request - parsing error int status=rresponse.getStatus(); // log( "New request " + rrequest ); // System.out.print(A); if( status < 400 ) status= processRequest( rrequest ); if(status==0) status=authenticate( rrequest, rresponse ); if(status == 0) status=authorize( rrequest, rresponse ); if( status == 0 ) { rrequest.getWrapper().handleRequest(rrequest, rresponse); } else { // something went wrong

handleError( rrequest, rresponse, null, status ); } } catch (Throwable t) { handleError( rrequest, rresponse, t, 0 ); } // System.out.print("B"); 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 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 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 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 capture its consistent usage by other objects TraceSupport 94 CASCON '04 using an aspect captures the consistent usage of the tracing support by the objects TraceSupport tracing using a library aspect abstract aspect Tracing { abstract pointcut trace(); aspect BigBoxCoTracing { before(): trace() { TraceSupport.traceEntry(tjp);

} after(): trace() { TraceSupport.traceExit(tjp); } pointcut trace(): within(com.bigboxco.*) && execution(* *(..)); before(): trace() { TraceSupport.traceEntry( tjp); } after(): trace() { TraceSupport.traceExit( tjp); } } 95 CASCON '04 } aspect BigBoxCoTracing extends Tracing { pointcut trace(): within(com.bigboxco.*) && execution(* *(..)); } example context-passing aspects caller1 Service caller2 workers need to know the caller: capabilities charge backs to customize result worker 1 96 CASCON '04 worker 2 worker 3 context-passing aspects caller1

Service caller2 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.doService(String)); 98 CASCON '04 context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service.doService(String)); pointcut workPoints(Worker w): target(w) && call(void Worker.doTask(Task)); 99 CASCON '04 context-passing aspects pointcut invocations(Caller c): this(c) && call(void Service.doService(String)); pointcut workPoints(Worker w): target(w) && call(void Worker.doTask(Task)); pointcut perCallerWork(Caller c, Worker w): cflow(invocations(c)) && workPoints(w); 100 CASCON '04 context-passing aspects abstract aspect CapabilityChecking { pointcut invocations(Caller c): this(c) && call(void Service.doService(String)); pointcut workPoints(Worker w): target(w) && call(void Worker.doTask(Task)); pointcut perCallerWork(Caller c, Worker w):

cflow(invocations(c)) && workPoints(w); before (Caller c, Worker w): perCallerWork(c, w) { w.checkCapabilities(c); } } 101 CASCON '04 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 more than beginners expect aspect A { before(): call(String toString()) { System.err.println(tjp); } } use within or cflow to control circularity 103 aspect A { before(): call(String toString()) && !within(A) { System.err.println(tjp); } } CASCON '04 summary so far presented examples of aspects in design intuitions for identifying aspects presented implementations in AspectJ how the language support can help raised some style issues objects vs. aspects

104 CASCON '04 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 without AOP would lead to distant places in the code that do the same thing e.g. traceEntry(Point.set) try grep to find these [Griswold] do a coordinated single thing e.g. timing, observer pattern harder to find these 106 CASCON '04 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 traceEntry 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, measured by 108 CASCON '04 code size tangling coupling etc. learned through experience, influenced by taste and style 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 Part IV conclusion AOSD language design more dynamic crosscuts, type system tools more IDE support, aspect discovery, refactoring, re-cutting, 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 AspectJ technology AspectJ is a small extension to Java valid Java programs are also valid AspectJ programs AspectJ 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) AspectJ tools support IDE extensions: Emacs, JBuilder, NetBeans, Eclipse ant tasks works with existing debuggers license compiler, runtime and tools are Open Source and free for any use 113 CASCON '04 AspectJ on the web eclipse.org/aspectj 114 CASCON '04 documentation downloads user mailing list developer mailing list pointers elsewhere... summary functions OOP AOP handles greater complexity, provides more flexibility

crosscutting modularity AspectJ incremental adoption package revolutionary benefits free AspectJ tools community training, consulting, and support for use 115 CASCON '04 credits AspectJ 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

Recently Viewed Presentations

  • DR Monitorに必要なネットワーク・タイミング系

    DR Monitorに必要なネットワーク・タイミング系

    DR Monitorに必要なタイミング・ネットワークその他. 2016/10/07 DR 打ち合わせ. Ikeda,Koiso, Kobayashi, Sato, Tobiyama, Nakamura, Furukawa, Miyahara
  • Student Services Initiative: An Opportunity for System Change

    Student Services Initiative: An Opportunity for System Change

    (respecting PHIA and FIPPA) a summary of diagnostic and assessment information (prior and current) a summary of interests, strengths and learning styles. information about current levels of performance and academic program. areas to investigate. priority learning needs identified and agreed...
  • A Bloody Conflict - PC&#92;|MAC

    A Bloody Conflict - PC\|MAC

    Title: A Bloody Conflict Author: April Cash Created Date: 2/20/2007 8:05:50 PM Document presentation format: On-screen Show (4:3) Other titles: Tahoma Arial Wingdings Calibri Slit 1_Slit 2_Slit The Great War Trench Warfare German soldiers attacking "No Man's Land" German solider...
  • Bond Valuation - Duke University

    Bond Valuation - Duke University

    Bond Valuation. Fuqua School of Business. Duke University. Definition of a Bond. A . ... Perpetual Bonds (Consols) No maturity date. Pay a stated coupon at periodic intervals. Types of Bonds. Self-Amortizing Bonds . Pay a regular fixed amount each...
  • Decadal variations of Sun-like stars (&quot;solar variability ...

    Decadal variations of Sun-like stars ("solar variability ...

    Variability of the "solar twins" b (472 nm ) y (571 nm) A few stars have "negative" net variance due to comp. star variability. This is a key result, the trend of rms variability, year to year in the log...
  • x vDPA for Vhost Acceleration XIAO WANG, Intel

    x vDPA for Vhost Acceleration XIAO WANG, Intel

    Agenda. VhostEvolution. An Ideal Vhost HW ACC Model. vDPADesign and Impl. DPDK VhostLib and vDPA Driver. QEMU VhostUser Extention. Upstreaming Status. AccVhostwith vDPA
  • S.p.o.c.

    S.p.o.c.

    Solar Power station for the model S. Electric car using charge stations. Cost: $62,400 (1,170 for Vehicle Dock) Super Charge station incorporate PV cells (Separate Assembly) Possibly spread out to more slide. Cameron. s, too crowded
  • The Wallace Foundation

    The Wallace Foundation

    The goal should be for each local school districts to have a lively ecosystem for talent development that ensures all schools bring on board excellent principals and then support them on the job. -- Leslie M. Anderson and Brenda J....