没有合适的资源?快使用搜索试试~ 我知道了~
首页使用AspectJ实现面向切面编程
使用AspectJ实现面向切面编程
需积分: 6 5 下载量 171 浏览量
更新于2024-07-31
收藏 1.33MB PPT 举报
"Aspect-Oriented Programming with AspectJ"
Aspect-Oriented Programming(AOP)是一种编程范式,旨在解决软件开发中的横切关注点(cross-cutting concerns),如日志、事务管理、性能监控等,这些关注点往往分散在程序的多个模块中,使得代码难以维护和复用。AspectJ是Java平台上的一个开源AOP框架,它通过提供一种语言扩展,允许开发者将这些横切关注点封装到独立的模块——方面(aspects)中,从而实现代码的高内聚和低耦合。
AspectJ的主要概念包括:
1. **切面(Aspects)**:切面是封装了横切关注点的模块,它将分散的代码逻辑集中在一起,提高了代码的可读性和可维护性。
2. **连接点(Join Points)**:连接点是在程序执行过程中可以插入切面的一个特定点,如方法调用、异常处理等。
3. **通知(Advises)**:通知是在特定连接点上执行的代码,它定义了切面的行为。
4. **切入点(Pointcuts)**:切入点是匹配一组特定连接点的表达式,它定义了哪些连接点上应用通知。
5. **织入(Weaving)**:织入是将切面与主程序代码结合的过程,可以在编译时、类加载时或运行时完成。
AspectJ提供了多种语言机制来实现AOP,例如:
- **注解(Annotations)**:使用注解可以在不改变原有代码结构的情况下声明切面。
- **引入(Introductions)**:引入允许添加新的接口或属性到现有的类型。
- **环绕通知(Around Advises)**:环绕通知允许在连接点前后完全控制程序流程。
在实际应用中,AspectJ可以显著提高代码的模块化程度。例如,在Tomcat服务器中,XML解析和URL模式匹配是核心功能,它们可以被很好地封装在各自的类中,形成良好的模块化。然而,当涉及到日志记录这样的横切关注点时,相关的代码会散落在很多地方,这使得日志模块化变得困难。AspectJ可以将日志记录抽取到一个单独的切面中,这样既不会破坏原有模块的功能,又能确保日志处理的统一性和一致性。
通过AspectJ的教程,开发者可以学习如何创建第一个切面示例,理解各种语言机制,并学会如何在实际项目中应用切面。教程中可能包含逐步指导,演示如何定义切入点,编写通知,并将切面织入到应用程序中。
AspectJ为解决复杂系统中的模块化问题提供了强大的工具,使得开发者能够更好地组织代码,提高软件的可维护性和可扩展性。在CASCON'04会议上,Julie Waterhouse和Mik Kersten通过实例和演示进一步阐述了AspectJ的使用,展示了其在提升代码结构和设计质量方面的优势。
CASCON '04
11
language support to…
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software
itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products
derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.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.*;
/**
* Core implementation of an application level session
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author Jason Hunter [jch@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
*/
public class ApplicationSession implements HttpSession {
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;
ApplicationSession(String id, ServerSession serverSession,
Context context) {
this.serverSession = serverSession;
this.context = context;
this.id = id;
this.inactiveInterval = context.getSessionTimeOut();
if (this.inactiveInterval != -1) {
this.inactiveInterval *= 60;
}
}
ServerSession getServerSession() {
return serverSession;
}
/**
* Called by context when request comes in so that accesses and
* inactivities can be dealt with accordingly.
*/
void accessed() {
// set last accessed to thisAccessTime as it will be left over
// from the previous access
lastAccessed = thisAccessTime;
thisAccessTime = System.currentTimeMillis();
validate();
}
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;
if (thisInterval > inactiveInterval) {
invalidate();
}
}
}
// HTTP SESSION IMPLEMENTATION METHODS
public String getId() {
if (valid) {
return id;
} else {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
}
public long getCreationTime() {
if (valid) {
return creationTime;
} else {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
}
/**
*
* @deprecated
*/
public HttpSessionContext getSessionContext() {
return new SessionContextImpl();
}
public long getLastAccessedTime() {
if (valid) {
return lastAccessed;
} else {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
}
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");
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); // remove any existing binding
if (value != null && value instanceof HttpSessionBindingListener) {
HttpSessionBindingEvent e =
new HttpSessionBindingEvent(this, name);
((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");
throw new IllegalStateException(msg);
}
if (name == null) {
String msg = sm.getString("applicationSession.value.iae");
throw new IllegalArgumentException(msg);
}
return values.get(name);
}
/**
* @deprecated
*/
public String[] getValueNames() {
Enumeration e = getAttributeNames();
Vector names = new Vector();
while (e.hasMoreElements()) {
names.addElement(e.nextElement());
}
String[] valueNames = new String[names.size()];
names.copyInto(valueNames);
return valueNames;
}
public Enumeration getAttributeNames() {
if (! valid) {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
Hashtable valuesClone = (Hashtable)values.clone();
return (Enumeration)valuesClone.keys();
}
/**
* @deprecated
*/
public void removeValue(String name) {
removeAttribute(name);
}
public void removeAttribute(String name) {
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);
}
Object o = values.get(name);
if (o instanceof HttpSessionBindingListener) {
HttpSessionBindingEvent e =
new HttpSessionBindingEvent(this,name);
((HttpSessionBindingListener)o).valueUnbound(e);
}
values.remove(name);
}
public void setMaxInactiveInterval(int interval) {
if (! valid) {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
inactiveInterval = interval;
}
public int getMaxInactiveInterval() {
if (! valid) {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
return inactiveInterval;
}
}
//-----------------------------------------------------------------------
ApplicationSession
package org.apache.tomcat.session;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
import org.apache.tomcat.catalina.*;
import org.apache.tomcat.util.StringManager;
/**
* Standard implementation of the <b>Session</b>
interface. This object is
* serializable, so that it can be stored in persistent
storage or transferred
* to a different JVM for distributable session support.
* <p>
* <b>IMPLEMENTATION NOTE</b>: An instance of this
class represents both the
* internal (Session) and application level
(HttpSession) view of the session.
* However, because the class itself is not declared
public, Java logic outside
* of the <code>org.apache.tomcat.session</code> package
cannot cast an
* 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
$
*/
final class StandardSession
implements HttpSession, Session {
//
--------------------------------------------------------
--- Constructors
/**
* Construct a new Session associated with the
specified Manager.
*
* @param manager The manager with which this
Session is associated
*/
public StandardSession(Manager manager) {
super();
this.manager = manager;
}
//
-----------------------------------------------------
Instance Variables
/**
* The collection of user data attributes associated
with this Session.
*/
private Hashtable attributes = new Hashtable();
/**
* The time this session was created, in
milliseconds since midnight,
* January 1, 1970 GMT.
*/
private long creationTime = 0L;
/**
* The session identifier of this Session.
*/
private String id = null;
/**
* Descriptive information describing this Session
implementation.
*/
private static final String info =
"StandardSession/1.0";
/**
* The last accessed time for this Session.
*/
private long lastAccessedTime = creationTime;
/**
* The Manager with which this Session is
associated.
*/
private Manager manager = null;
/**
* The maximum time interval, in seconds, between
client requests before
* the servlet container may invalidate this
session. A negative time
* indicates that the session should never time out.
*/
private int maxInactiveInterval = -1;
/**
* Flag indicating whether this session is new or
not.
*/
private boolean isNew = true;
/**
* Flag indicating whether this session is valid or
not.
*/
private boolean isValid = false;
/**
* The string manager for this package.
*/
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
/**
* The HTTP session context associated with this
session.
*/
private static HttpSessionContext sessionContext =
null;
/**
* The current accessed time for this session.
*/
private long thisAccessedTime = creationTime;
//
-----------------------------------------------------
Session Properties
/**
* Set the creation time for this session. This
method is called by the
* Manager when an existing Session instance is
reused.
*
* @param time The new creation time
*/
public void setCreationTime(long time) {
this.creationTime = time;
this.lastAccessedTime = time;
this.thisAccessedTime = time;
}
/**
* Return the session identifier for this session.
*/
public String getId() {
return (this.id);
}
/**
* Set the session identifier for this session.
*
* @param id The new session identifier
*/
public void setId(String id) {
if ((this.id != null) && (manager != null) &&
(manager instanceof ManagerBase))
((ManagerBase) manager).remove(this);
this.id = id;
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
* <code><description>/<version></code>.
*/
public String getInfo() {
return (this.info);
}
/**
* 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 (this.lastAccessedTime);
}
/**
* Return the Manager within which this Session is
valid.
*/
public Manager getManager() {
return (this.manager);
}
/**
* Set the Manager within which this Session is
valid.
*
* @param manager The new Manager
*/
public void setManager(Manager manager) {
this.manager = manager;
}
/**
* Return the maximum time interval, in seconds,
between client requests
* before the servlet container will invalidate the
session. A negative
* time indicates that the session should never time
out.
*
* @exception IllegalStateException if this method
is called on
* an invalidated session
*/
public int getMaxInactiveInterval() {
return (this.maxInactiveInterval);
}
/**
* 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 the <code>HttpSession</code> for which this object
* is the facade.
*/
public HttpSession getSession() {
return ((HttpSession) this);
}
// ------------------------------------------------- Session
Public Methods
/**
* 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() {
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() {
// Remove this session from our manager's active sessions
if ((manager != null) && (manager instanceof ManagerBase))
((ManagerBase) manager).remove(this);
// 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);
}
// Mark this session as invalid
setValid(false);
}
/**
* 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
attributes.clear();
creationTime = 0L;
id = null;
lastAccessedTime = 0L;
manager = null;
maxInactiveInterval = -1;
isNew = true;
isValid = false;
// Tell our Manager that this Session has been recycled
if ((manager != null) && (manager instanceof ManagerBase))
((ManagerBase) manager).recycle(this);
}
// ------------------------------------------------ Session
Package Methods
/**
* Return the <code>isValid</code> flag for this session.
*/
boolean isValid() {
return (this.isValid);
}
/**
* Set the <code>isNew</code> flag for this session.
*
* @param isNew The new value for the <code>isNew</code> flag
*/
void setNew(boolean isNew) {
this.isNew = isNew;
}
/**
* Set the <code>isValid</code> flag for this session.
*
* @param isValid The new value for the <code>isValid</code>
flag
*/
void setValid(boolean isValid) {
this.isValid = isValid;
}
// -------------------------------------------------
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() {
return (this.creationTime);
}
/**
* 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() {
if (sessionContext == null)
sessionContext = new StandardSessionContext();
return (sessionContext);
}
// ----------------------------------------------HttpSession
Public Methods
/**
* Return the object bound with the specified name in this
session, or
* <code>null</code> if no object is bound with that name.
*
* @param name Name of the attribute to be returned
*
* @exception IllegalStateException if this method is called
on an
* invalidated session
*/
public Object getAttribute(String name) {
return (attributes.get(name));
}
/**
* Return an <code>Enumeration</code> of <code>String</code>
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() {
return (attributes.keys());
}
/**
* Return the object bound with the specified name in this
session, or
* <code>null</code> if no object is bound with that name.
*
* @param name Name of the value to be returned
*
* @exception IllegalStateException if this method is called
on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>getAttribute()</code>
*/
public Object getValue(String name) {
return (getAttribute(name));
}
/**
* 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
* <code>getAttributeNames()</code>
*/
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);
}
/**
* Invalidates this session and unbinds any objects bound to
it.
*
* @exception IllegalStateException if this method is called
on
* an invalidated session
*/
public void invalidate() {
// Cause this session to expire
expire();
}
/**
* Return <code>true</code> if the client does not yet know
about the
* session, or if the client chooses not to join the session.
For
* example, if the server used only cookie-based sessions, and
the client
* has disabled the use of cookies, then a session would be
new on each
* request.
*
* @exception IllegalStateException if this method is called
on an
* invalidated session
*/
public boolean isNew() {
return (this.isNew);
}
/**
* Bind an object to this session, using the specified name. If an object
* of the same name is already bound to this session, the object is
* replaced.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueBound()</code> on the object.
*
* @param name Name to which the object is bound, cannot be null
* @param value Object to be bound, cannot be null
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>setAttribute()</code>
*/
public void putValue(String name, Object value) {
setAttribute(name, value);
}
/**
* Remove the object bound with the specified name from this session. If
* the session does not have an object bound with this name, this method
* does nothing.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueUnbound()</code> 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) {
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));
}
}
}
/**
* Remove the object bound with the specified name from this session. If
* the session does not have an object bound with this name, this method
* does nothing.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueUnbound()</code> 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
* <code>removeAttribute()</code>
*/
public void removeValue(String name) {
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.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueBound()</code> on the object.
*
* @param name Name to which the object is bound, cannot be null
* @param value Object to be bound, cannot be null
*
* @exception 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) {
if ((manager != null) && manager.getDistributable() &&
!(value instanceof Serializable))
throw new IllegalArgumentException
(sm.getString("standardSession.setAttribute.iae"));
synchronized (attributes) {
removeAttribute(name);
attributes.put(name, value);
if (value instanceof HttpSessionBindingListener)
((HttpSessionBindingListener) value).valueBound
(new HttpSessionBindingEvent((HttpSession) this, name));
}
}
// -------------------------------------------- HttpSession Private Methods
/**
* Read a serialized version of this session object from the specified
* object input stream.
* <p>
* <b>IMPLEMENTATION NOTE</b>: The reference to the owning Manager
* is not restored by this method, and must be set explicitly.
*
* @param stream The input stream to read from
*
* @exception ClassNotFoundException if an unknown class is specified
* @exception IOException if an input/output error occurs
*/
private void readObject(ObjectInputStream stream)
throws ClassNotFoundException, IOException {
// 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();
// 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);
}
}
/**
* Write a serialized version of this session object to the specified
* object output stream.
* <p>
* <b>IMPLEMENTATION NOTE</b>: The owning Manager will not be stored
* in the serialized representation of this Session. After calling
* <code>readObject()</code>, you must set the associated Manager
* explicitly.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Any attribute that is not Serializable
* will be silently ignored. If you do not want any such attributes,
* be sure the <code>distributable</code> property of our associated
* Manager is set to <code>true</code>.
*
* @param stream The output stream to write to
*
* @exception IOException if an input/output error occurs
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
// 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));
// 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);
}
// 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));
}
}
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));
static advice(StandardSession s): invalidate(s) {
before {
if (!s.isValid())
throw new IllegalStateException
(s.sm.getString("standardSession."
+ thisJoinPoint.methodName
+ ".ise"));
}
}
}
// -------------------------------------------------------------- Private Class
/**
* This class is a dummy implementation of the <code>HttpSessionContext</code>
* interface, to conform to the requirement that such an object be returned
* when <code>HttpSession.getSessionContext()</code> 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.
*/
final class StandardSessionContext implements HttpSessionContext {
private Vector dummy = new Vector();
/**
* Return the session identifiers of all sessions defined
* within this context.
*
* @deprecated As of Java Servlet API 2.1 with no replacement.
* This method must return an empty <code>Enumeration</code>
* and will be removed in a future version of the API.
*/
public Enumeration getIds() {
return (dummy.elements());
}
/**
* Return the <code>HttpSession</code> associated with the
* specified session identifier.
*
* @param id Session identifier for which to look up a session
*
* @deprecated As of Java Servlet API 2.1 with no replacement.
* This method must return null and will be removed in a
* future version of the API.
*/
public HttpSession getSession(String id) {
return (null);
}
}
StandardSession
package org.apache.tomcat.session;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tomcat.catalina.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.apache.tomcat.util.StringManager;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* Standard implementation of the <b>Manager</b> interface that provides
* no session persistence or distributable capabilities, but does support
* an optional, configurable, maximum number of active sessions allowed.
* <p>
* Lifecycle configuration of this component assumes an XML node
* in the following format:
* <code>
* <Manager className="org.apache.tomcat.session.StandardManager"
* checkInterval="60" maxActiveSessions="-1"
* maxInactiveInterval="-1" />
* </code>
* where you can adjust the following parameters, with default values
* in square brackets:
* <ul>
* <li><b>checkInterval</b> - The interval (in seconds) between background
* thread checks for expired sessions. [60]
* <li><b>maxActiveSessions</b> - The maximum number of sessions allowed to
* be active at once, or -1 for no limit. [-1]
* <li><b>maxInactiveInterval</b> - The default maximum number of seconds of
* inactivity before which the servlet container is allowed to time out
* a session, or -1 for no limit. This value should be overridden from
* the default session timeout specified in the web application deployment
* descriptor, if any. [-1]
* </ul>
*
* @author Craig R. McClanahan
* @version $Revision: 1.1.1.1 $ $Date: 2000/05/02 21:28:30 $
*/
public final class StandardManager
extends ManagerBase
implements Lifecycle, Runnable {
// ----------------------------------------------------- Instance Variables
/**
* The interval (in seconds) between checks for expired sessions.
*/
private int checkInterval = 60;
/**
* Has this component been configured yet?
*/
private boolean configured = false;
/**
* The descriptive information about this implementation.
*/
private static final String info = "StandardManager/1.0";
/**
* The maximum number of active Sessions allowed, or -1 for no limit.
*/
protected int maxActiveSessions = -1;
/**
* The string manager for this package.
*/
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
/**
* Has this component been started yet?
*/
private boolean started = false;
/**
* The background thread.
*/
private Thread thread = null;
/**
* The background thread completion semaphore.
*/
private boolean threadDone = false;
/**
* Name to register for the background thread.
*/
private String threadName = "StandardManager";
// ------------------------------------------------------------- Properties
/**
* Return the check interval (in seconds) for this Manager.
*/
public int getCheckInterval() {
return (this.checkInterval);
}
/**
* Set the check interval (in seconds) for this Manager.
*
* @param checkInterval The new check interval
*/
public void setCheckInterval(int checkInterval) {
this.checkInterval = checkInterval;
}
/**
* Return descriptive information about this Manager implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo() {
return (this.info);
}
/**
* Return the maximum number of active Sessions allowed, or -1 for
* no limit.
*/
public int getMaxActiveSessions() {
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) {
this.maxActiveSessions = max;
}
// --------------------------------------------------------- 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 <code>null</code>.
*
* @exception IllegalStateException if a new session cannot be
* instantiated for any reason
*/
public Session createSession() {
if ((maxActiveSessions >= 0) &&
(sessions.size() >= maxActiveSessions))
throw new IllegalStateException
(sm.getString("standardManager.createSession.ise"));
return (super.createSession());
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Configure this component, based on the specified configuration
* parameters. This method should be called immediately after the
* component instance is created, and before <code>start()</code>
* is called.
*
* @param parameters Configuration parameters for this component
* (<B>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 {
// Validate and update our current component state
if (configured)
throw new LifecycleException
(sm.getString("standardManager.alreadyConfigured"));
configured = true;
if (parameters == null)
return;
// Parse and process our configuration parameters
if (!("Manager".equals(parameters.getNodeName())))
return;
NamedNodeMap attributes = parameters.getAttributes();
Node node = null;
node = attributes.getNamedItem("checkInterval");
if (node != null) {
try {
setCheckInterval(Integer.parseInt(node.getNodeValue()));
} catch (Throwable t) {
; // XXX - Throw exception?
}
}
node = attributes.getNamedItem("maxActiveSessions");
if (node != null) {
try {
setMaxActiveSessions(Integer.parseInt(node.getNodeValue()));
} catch (Throwable t) {
; // XXX - Throw exception?
}
}
node = attributes.getNamedItem("maxInactiveInterval");
if (node != null) {
try {
setMaxInactiveInterval(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 <code>configure()</code>,
* 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 {
// 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;
// Start the background reaper thread
threadStart();
}
/**
* 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 {
// Validate and update our current component state
if (!started)
throw new LifecycleException
(sm.getString("standardManager.notStarted"));
started = false;
// 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();
}
}
// -------------------------------------------------------- Private Methods
/**
* Invalidate all sessions that have expired.
*/
private void processExpires() {
long timeNow = System.currentTimeMillis();
Session sessions[] = findSessions();
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();
}
}
/**
* Sleep for the duration specified by the <code>checkInterval</code>
* property.
*/
private void threadSleep() {
try {
Thread.sleep(checkInterval * 1000L);
} catch (InterruptedException e) {
;
}
}
/**
* 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();
}
/**
* Stop the background thread that is periodically checking for
* session timeouts.
*/
private void threadStop() {
if (thread == null)
return;
threadDone = true;
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
;
}
thread = null;
}
// ------------------------------------------------------ Background Thread
/**
* The background thread that checks for session timeouts and shutdown.
*/
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}
}
StandardManager StandardSessionManager
package org.apache.tomcat.session;
import java.io.IOException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.apache.tomcat.catalina.*;
import org.apache.tomcat.core.Context;
import org.apache.tomcat.core.Request;
import org.apache.tomcat.core.Response;
import org.apache.tomcat.core.SessionManager;
import org.apache.tomcat.util.SessionUtil;
/**
* Specialized implementation of org.apache.tomcat.core.SessionManager
* that adapts to the new component-based Manager implementation.
* <p>
* XXX - At present, use of <code>StandardManager</code> is hard coded,
* and lifecycle configuration is not supported.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Once we commit to the new Manager/Session
* paradigm, I would suggest moving the logic implemented here back into
* the core level. The Tomcat.Next "Manager" interface acts more like a
* collection class, and has minimal knowledge of the detailed request
* processing semantics of handling sessions.
* <p>
* 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
* timeout for this web application (specified in the deployment descriptor)
* should be.
*
* @author Craig R. McClanahan
*/
public final class StandardSessionManager
implements SessionManager {
// -----------------------------------------------------------
Constructors
/**
* Create a new SessionManager that adapts to the corresponding Manager
* implementation.
*/
public StandardSessionManager() {
manager = new StandardManager();
if (manager instanceof Lifecycle) {
try {
((Lifecycle) manager).configure(null);
((Lifecycle) manager).start();
} catch (LifecycleException e) {
throw new IllegalStateException("" + e);
}
}
}
// ----------------------------------------------------- Instance
Variables
/**
* The Manager implementation we are actually using.
*/
private Manager manager = null;
// --------------------------------------------------------- Public Methods
/**
* Mark the specified session's last accessed time. This should be
* called for each request by a RequestInterceptor.
*
* @param session The session to be marked
*/
public void accessed(Context ctx, Request req, String id) {
HttpSession session=findSession(ctx, id);
if( session == null) return;
if (session instanceof Session)
((Session) session).access();
// cache the HttpSession - avoid another find
req.setSession( session );
}
// XXX should we throw exception or just return null ??
public HttpSession findSession( Context ctx, String id ) {
try {
Session session = manager.findSession(id);
if(session!=null)
return session.getSession();
} catch (IOException e) {
}
return (null);
}
public HttpSession createSession(Context ctx) {
return manager.createSession().getSession();
}
/**
* Remove all sessions because our associated Context is being shut
down.
*
* @param ctx The context that is being shut down
*/
public void removeSessions(Context ctx) {
// XXX XXX a manager may be shared by multiple
// contexts, we just want to remove the sessions of ctx!
// The manager will still run after that ( i.e. keep database
// connection open
if (manager instanceof Lifecycle) {
try {
((Lifecycle) manager).stop();
} 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.
*
* @param minutes The session inactivity timeout in minutes.
*/
public void setSessionTimeOut(int minutes) {
if(-1 != minutes) {
// The manager works with seconds...
manager.setMaxInactiveInterval(minutes * 60);
}
}
}
ServerSessionManager
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 [duncan@eng.sun.com]
* @author Jason Hunter [jch@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
*/
public class ServerSessionManager implements SessionManager {
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
private static ServerSessionManager manager; // = new ServerSessionManager();
protected int inactiveInterval = -1;
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 accessed( Context ctx, Request req, String id ) {
ApplicationSession apS=(ApplicationSession)findSession( ctx, id);
if( apS==null) return;
ServerSession servS=apS.getServerSession();
servS.accessed();
apS.accessed();
// cache it - no need to compute it again
req.setSession( apS );
}
public HttpSession createSession(Context ctx) {
String sessionId = SessionIdGenerator.generateId();
ServerSession session = new ServerSession(sessionId);
sessions.put(sessionId, session);
if(-1 != inactiveInterval) {
session.setMaxInactiveInterval(inactiveInterval);
}
return session.getApplicationSession( ctx, true );
}
public HttpSession findSession(Context ctx, String id) {
ServerSession sSession=(ServerSession)sessions.get(id);
if(sSession==null) return null;
return sSession.getApplicationSession(ctx, false);
}
// 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.
synchronized void reap() {
Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ServerSession session = (ServerSession)sessions.get(key);
session.reap();
session.validate();
}
}
synchronized void removeSession(ServerSession session) {
String id = session.getId();
session.invalidate();
sessions.remove(id);
}
public void removeSessions(Context context) {
Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ServerSession session = (ServerSession)sessions.get(key);
ApplicationSession appSession =
session.getApplicationSession(context, false);
if (appSession != null) {
appSession.invalidate();
}
}
}
/**
* 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.
*
* @param minutes The session inactivity timeout in minutes.
*/
public void setSessionTimeOut(int minutes) {
if(-1 != minutes) {
// The manager works with seconds...
inactiveInterval = (minutes * 60);
}
}
}
SessionInterceptor
package org.apache.tomcat.request;
import org.apache.tomcat.core.*;
import org.apache.tomcat.util.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.http.*;
/**
* Will process the request and determine the session Id, and set it
* in the Request.
* It also marks the session as accessed.
*
* This implementation only handles Cookies sessions, please extend or
* add new interceptors for other methods.
*
*/
public class SessionInterceptor extends BaseInterceptor implements RequestInterceptor {
// GS, separates the session id from the jvm route
static final char SESSIONID_ROUTE_SEP = '.';
int debug=0;
ContextManager cm;
public SessionInterceptor() {
}
public void setDebug( int i ) {
System.out.println("Set debug to " + i);
debug=i;
}
public void setContextManager( ContextManager cm ) {
this.cm=cm;
}
public int requestMap(Request request ) {
String sessionId = null;
Cookie cookies[]=request.getCookies(); // assert !=null
for( int i=0; i<cookies.length; i++ ) {
Cookie cookie = cookies[i];
if (cookie.getName().equals("JSESSIONID")) {
sessionId = cookie.getValue();
sessionId=validateSessionId(request, sessionId);
if (sessionId!=null){
request.setRequestedSessionIdFromCookie(true);
}
}
}
String sig=";jsessionid=";
int foundAt=-1;
if( debug>0 ) cm.log(" XXX RURI=" + request.getRequestURI());
if ((foundAt=request.getRequestURI().indexOf(sig))!=-1){
sessionId=request.getRequestURI().substring(foundAt+sig.length());
// rewrite URL, do I need to do anything more?
request.setRequestURI(request.getRequestURI().substring(0, foundAt));
sessionId=validateSessionId(request, sessionId);
if (sessionId!=null){
request.setRequestedSessionIdFromURL(true);
}
}
return 0;
}
// XXX what is the correct behavior if the session is invalid ?
// We may still set it and just return session invalid.
/** Validate and fix the session id. If the session is not valid return null.
* It will also clean up the session from load-balancing strings.
* @return sessionId, or null if not valid
*/
private String validateSessionId(Request request, String sessionId){
// GS, We piggyback the JVM id on top of the session cookie
// Separate them ...
if( debug>0 ) cm.log(" Orig sessionId " + sessionId );
if (null != sessionId) {
int idex = sessionId.lastIndexOf(SESSIONID_ROUTE_SEP);
if(idex > 0) {
sessionId = sessionId.substring(0, idex);
}
}
if (sessionId != null && sessionId.length()!=0) {
// GS, We are in a problem here, we may actually get
// multiple Session cookies (one for the root
// context and one for the real context... or old session
// cookie. We must check for validity in the current context.
Context ctx=request.getContext();
SessionManager sM = ctx.getSessionManager();
if(null != sM.findSession(ctx, sessionId)) {
sM.accessed(ctx, request, sessionId );
request.setRequestedSessionId(sessionId);
if( debug>0 ) cm.log(" Final session id " + sessionId );
return sessionId;
}
}
return null;
}
public int beforeBody( Request rrequest, Response response ) {
String reqSessionId = response.getSessionId();
if( debug>0 ) cm.log("Before Body " + reqSessionId );
if( reqSessionId==null)
return 0;
// GS, set the path attribute to the cookie. This way
// multiple session cookies can be used, one for each
// context.
String sessionPath = rrequest.getContext().getPath();
if(sessionPath.length() == 0) {
sessionPath = "/";
}
// GS, piggyback the jvm route on the session id.
if(!sessionPath.equals("/")) {
String jvmRoute = rrequest.getJvmRoute();
if(null != jvmRoute) {
reqSessionId = reqSessionId + SESSIONID_ROUTE_SEP + jvmRoute;
}
}
Cookie cookie = new Cookie("JSESSIONID",
reqSessionId);
cookie.setMaxAge(-1);
cookie.setPath(sessionPath);
cookie.setVersion(1);
response.addHeader( CookieTools.getCookieHeaderName(cookie),
CookieTools.getCookieHeaderValue(cookie));
cookie.setVersion(0);
response.addHeader( CookieTools.getCookieHeaderName(cookie),
CookieTools.getCookieHeaderValue(cookie));
return 0;
}
/** Notification of context shutdown
*/
public void contextShutdown( Context ctx )
throws TomcatException
{
if( ctx.getDebug() > 0 ) ctx.log("Removing sessions from " + ctx );
ctx.getSessionManager().removeSessions(ctx);
}
}
ServerSession
package org.apache.tomcat.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.*;
/**
* Core implementation of a server session
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
*/
public class ServerSession {
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
private Hashtable values = new Hashtable();
private Hashtable appSessions = new Hashtable();
private String id;
private long creationTime = System.currentTimeMillis();;
private long thisAccessTime = creationTime;
private long lastAccessed = creationTime;
private int inactiveInterval = -1;
ServerSession(String id) {
this.id = id;
}
public String getId() {
return id;
}
public long getCreationTime() {
return creationTime;
}
public long getLastAccessedTime() {
return lastAccessed;
}
public ApplicationSession getApplicationSession(Context context,
boolean create) {
ApplicationSession appSession =
(ApplicationSession)appSessions.get(context);
if (appSession == null && create) {
// XXX
// sync to ensure valid?
appSession = new ApplicationSession(id, this, context);
appSessions.put(context, appSession);
}
// XXX
// make sure that we haven't gone over the end of our
// inactive interval -- if so, invalidate and create
// a new appSession
return appSession;
}
void removeApplicationSession(Context context) {
appSessions.remove(context);
}
/**
* Called by context when request comes in so that accesses and
* inactivities can be dealt with accordingly.
*/
void accessed() {
// set last accessed to thisAccessTime as it will be left over
// from the previous access
lastAccessed = thisAccessTime;
thisAccessTime = System.currentTimeMillis();
}
void validate()
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;
if (thisInterval > inactiveInterval) {
invalidate();
ServerSessionManager ssm =
ServerSessionManager.getManager();
ssm.removeSession(this);
}
}
}
synchronized void invalidate() {
Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ApplicationSession appSession =
(ApplicationSession)appSessions.get(key);
appSession.invalidate();
}
}
public void putValue(String name, Object value) {
if (name == null) {
String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg);
}
removeValue(name); // remove any existing binding
values.put(name, value);
}
public Object getValue(String name) {
if (name == null) {
String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg);
}
return values.get(name);
}
public Enumeration getValueNames() {
return values.keys();
}
public void removeValue(String name) {
values.remove(name);
}
public void setMaxInactiveInterval(int interval) {
inactiveInterval = interval;
}
public int getMaxInactiveInterval() {
return inactiveInterval;
}
// 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.
synchronized void reap() {
Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ApplicationSession appSession =
(ApplicationSession)appSessions.get(key);
appSession.validate();
}
}
}
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software
itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products
derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 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
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.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.*;
/**
* Core implementation of an application level session
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author Jason Hunter [jch@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
*/
public class ApplicationSession implements HttpSession {
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 boolean valid = true;
ApplicationSession(String id, ServerSession serverSession,
Context context) {
this.serverSession = serverSession;
this.context = context;
this.id = id;
this.inactiveInterval = context.getSessionTimeOut();
if (this.inactiveInterval != -1) {
this.inactiveInterval *= 60;
}
}
ServerSession getServerSession() {
return serverSession;
}
/**
* Called by context when request comes in so that accesses and
* inactivities can be dealt with accordingly.
*/
// HTTP SESSION IMPLEMENTATION METHODS
public String getId() {
if (valid) {
return id;
} else {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
}
public long getCreationTime() {
if (valid) {
return creationTime;
} else {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
}
/**
*
* @deprecated
*/
public HttpSessionContext getSessionContext() {
return new SessionContextImpl();
}
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");
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); // remove any existing binding
if (value != null && value instanceof HttpSessionBindingListener) {
HttpSessionBindingEvent e =
new HttpSessionBindingEvent(this, name);
((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");
throw new IllegalStateException(msg);
}
if (name == null) {
String msg = sm.getString("applicationSession.value.iae");
throw new IllegalArgumentException(msg);
}
return values.get(name);
}
/**
* @deprecated
*/
public String[] getValueNames() {
Enumeration e = getAttributeNames();
Vector names = new Vector();
while (e.hasMoreElements()) {
names.addElement(e.nextElement());
}
String[] valueNames = new String[names.size()];
names.copyInto(valueNames);
return valueNames;
}
public Enumeration getAttributeNames() {
if (! valid) {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
Hashtable valuesClone = (Hashtable)values.clone();
return (Enumeration)valuesClone.keys();
}
/**
* @deprecated
*/
public void removeValue(String name) {
removeAttribute(name);
}
public void removeAttribute(String name) {
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);
}
Object o = values.get(name);
if (o instanceof HttpSessionBindingListener) {
HttpSessionBindingEvent e =
new HttpSessionBindingEvent(this,name);
((HttpSessionBindingListener)o).valueUnbound(e);
}
values.remove(name);
}
public void setMaxInactiveInterval(int interval) {
if (! valid) {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
inactiveInterval = interval;
}
public int getMaxInactiveInterval() {
if (! valid) {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
return inactiveInterval;
}
}
//-----------------------------------------------------------------------
ApplicationSession
package org.apache.tomcat.session;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
import org.apache.tomcat.catalina.*;
import org.apache.tomcat.util.StringManager;
/**
* Standard implementation of the <b>Session</b>
interface. This object is
* serializable, so that it can be stored in persistent
storage or transferred
* to a different JVM for distributable session support.
* <p>
* <b>IMPLEMENTATION NOTE</b>: An instance of this
class represents both the
* internal (Session) and application level
(HttpSession) view of the session.
* However, because the class itself is not declared
public, Java logic outside
* of the <code>org.apache.tomcat.session</code> package
cannot cast an
* 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
$
*/
final class StandardSession
implements HttpSession, Session {
//
--------------------------------------------------------
--- Constructors
/**
* Construct a new Session associated with the
specified Manager.
*
* @param manager The manager with which this
Session is associated
*/
public StandardSession(Manager manager) {
super();
this.manager = manager;
}
//
-----------------------------------------------------
Instance Variables
/**
* The collection of user data attributes associated
with this Session.
*/
private Hashtable attributes = new Hashtable();
/**
* The time this session was created, in
milliseconds since midnight,
* January 1, 1970 GMT.
*/
private long creationTime = 0L;
/**
* The session identifier of this Session.
*/
private String id = null;
/**
* Descriptive information describing this Session
implementation.
*/
private static final String info =
"StandardSession/1.0";
/**
* The last accessed time for this Session.
*/
/**
* The Manager with which this Session is
associated.
*/
private Manager manager = null;
/**
* The maximum time interval, in seconds, between
client requests before
* the servlet container may invalidate this
session. A negative time
* indicates that the session should never time out.
*/
private int maxInactiveInterval = -1;
/**
* Flag indicating whether this session is new or
not.
*/
private boolean isNew = true;
/**
* Flag indicating whether this session is valid or
not.
*/
private boolean isValid = false;
/**
* The string manager for this package.
*/
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
/**
* The HTTP session context associated with this
session.
*/
private static HttpSessionContext sessionContext =
null;
/**
* The current accessed time for this session.
*/
private long thisAccessedTime = creationTime;
//
-----------------------------------------------------
Session Properties
/**
* Set the creation time for this session. This
method is called by the
* Manager when an existing Session instance is
reused.
*
* @param time The new creation time
*/
public void setCreationTime(long time) {
this.creationTime = time;
this.thisAccessedTime = time;
}
/**
* Return the session identifier for this session.
*/
public String getId() {
return (this.id);
}
/**
* Set the session identifier for this session.
*
* @param id The new session identifier
*/
public void setId(String id) {
if ((this.id != null) && (manager != null) &&
(manager instanceof ManagerBase))
((ManagerBase) manager).remove(this);
this.id = id;
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
* <code><description>/<version></code>.
*/
public String getInfo() {
return (this.info);
}
/**
* Return the Manager within which this Session is
valid.
*/
public Manager getManager() {
return (this.manager);
}
/**
* Set the Manager within which this Session is
valid.
*
* @param manager The new Manager
*/
public void setManager(Manager manager) {
this.manager = manager;
}
/**
* Return the maximum time interval, in seconds,
between client requests
* before the servlet container will invalidate the
session. A negative
* time indicates that the session should never time
out.
*
* @exception IllegalStateException if this method
is called on
* an invalidated session
*/
public int getMaxInactiveInterval() {
return (this.maxInactiveInterval);
}
/**
* 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 the <code>HttpSession</code> for which this object
* is the facade.
*/
public HttpSession getSession() {
return ((HttpSession) this);
}
// ------------------------------------------------- Session
Public Methods
/**
* Perform the internal processing required to invalidate this
session,
* without triggering an exception if the session has already
expired.
*/
public void expire() {
// Remove this session from our manager's active sessions
if ((manager != null) && (manager instanceof ManagerBase))
((ManagerBase) manager).remove(this);
// 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);
}
// Mark this session as invalid
setValid(false);
}
/**
* 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
attributes.clear();
creationTime = 0L;
id = null;
manager = null;
maxInactiveInterval = -1;
isNew = true;
isValid = false;
// Tell our Manager that this Session has been recycled
if ((manager != null) && (manager instanceof ManagerBase))
((ManagerBase) manager).recycle(this);
}
// ------------------------------------------------ Session
Package Methods
/**
* Return the <code>isValid</code> flag for this session.
*/
boolean isValid() {
return (this.isValid);
}
/**
* Set the <code>isNew</code> flag for this session.
*
* @param isNew The new value for the <code>isNew</code> flag
*/
void setNew(boolean isNew) {
this.isNew = isNew;
}
/**
* Set the <code>isValid</code> flag for this session.
*
* @param isValid The new value for the <code>isValid</code>
flag
*/
void setValid(boolean isValid) {
this.isValid = isValid;
}
// -------------------------------------------------
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() {
return (this.creationTime);
}
/**
* 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() {
if (sessionContext == null)
sessionContext = new StandardSessionContext();
return (sessionContext);
}
// ----------------------------------------------HttpSession
Public Methods
/**
* Return the object bound with the specified name in this
session, or
* <code>null</code> if no object is bound with that name.
*
* @param name Name of the attribute to be returned
*
* @exception IllegalStateException if this method is called
on an
* invalidated session
*/
public Object getAttribute(String name) {
return (attributes.get(name));
}
/**
* Return an <code>Enumeration</code> of <code>String</code>
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() {
return (attributes.keys());
}
/**
* Return the object bound with the specified name in this
session, or
* <code>null</code> if no object is bound with that name.
*
* @param name Name of the value to be returned
*
* @exception IllegalStateException if this method is called
on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>getAttribute()</code>
*/
public Object getValue(String name) {
return (getAttribute(name));
}
/**
* 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
* <code>getAttributeNames()</code>
*/
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);
}
/**
* Invalidates this session and unbinds any objects bound to
it.
*
* @exception IllegalStateException if this method is called
on
* an invalidated session
*/
public void invalidate() {
// Cause this session to expire
expire();
}
/**
* Return <code>true</code> if the client does not yet know
about the
* session, or if the client chooses not to join the session.
For
* example, if the server used only cookie-based sessions, and
the client
* has disabled the use of cookies, then a session would be
new on each
* request.
*
* @exception IllegalStateException if this method is called
on an
* invalidated session
*/
public boolean isNew() {
return (this.isNew);
}
/**
* Bind an object to this session, using the specified name. If an object
* of the same name is already bound to this session, the object is
* replaced.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueBound()</code> on the object.
*
* @param name Name to which the object is bound, cannot be null
* @param value Object to be bound, cannot be null
*
* @exception IllegalStateException if this method is called on an
* invalidated session
*
* @deprecated As of Version 2.2, this method is replaced by
* <code>setAttribute()</code>
*/
public void putValue(String name, Object value) {
setAttribute(name, value);
}
/**
* Remove the object bound with the specified name from this session. If
* the session does not have an object bound with this name, this method
* does nothing.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueUnbound()</code> 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) {
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));
}
}
}
/**
* Remove the object bound with the specified name from this session. If
* the session does not have an object bound with this name, this method
* does nothing.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueUnbound()</code> 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
* <code>removeAttribute()</code>
*/
public void removeValue(String name) {
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.
* <p>
* After this method executes, and if the object implements
* <code>HttpSessionBindingListener</code>, the container calls
* <code>valueBound()</code> on the object.
*
* @param name Name to which the object is bound, cannot be null
* @param value Object to be bound, cannot be null
*
* @exception 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) {
if ((manager != null) && manager.getDistributable() &&
!(value instanceof Serializable))
throw new IllegalArgumentException
(sm.getString("standardSession.setAttribute.iae"));
synchronized (attributes) {
removeAttribute(name);
attributes.put(name, value);
if (value instanceof HttpSessionBindingListener)
((HttpSessionBindingListener) value).valueBound
(new HttpSessionBindingEvent((HttpSession) this, name));
}
}
// -------------------------------------------- HttpSession Private Methods
/**
* Read a serialized version of this session object from the specified
* object input stream.
* <p>
* <b>IMPLEMENTATION NOTE</b>: The reference to the owning Manager
* is not restored by this method, and must be set explicitly.
*
* @param stream The input stream to read from
*
* @exception ClassNotFoundException if an unknown class is specified
* @exception IOException if an input/output error occurs
*/
private void readObject(ObjectInputStream stream)
throws ClassNotFoundException, IOException {
// Deserialize the scalar instance variables (except Manager)
creationTime = ((Long) stream.readObject()).longValue();
id = (String) stream.readObject();
isValid = ((Boolean) stream.readObject()).booleanValue();
// 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);
}
}
/**
* Write a serialized version of this session object to the specified
* object output stream.
* <p>
* <b>IMPLEMENTATION NOTE</b>: The owning Manager will not be stored
* in the serialized representation of this Session. After calling
* <code>readObject()</code>, you must set the associated Manager
* explicitly.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Any attribute that is not Serializable
* will be silently ignored. If you do not want any such attributes,
* be sure the <code>distributable</code> property of our associated
* Manager is set to <code>true</code>.
*
* @param stream The output stream to write to
*
* @exception IOException if an input/output error occurs
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
// Write the scalar instance variables (except Manager)
stream.writeObject(new Long(creationTime));
stream.writeObject(id);
stream.writeObject(new Integer(maxInactiveInterval));
stream.writeObject(new Boolean(isNew));
stream.writeObject(new Boolean(isValid));
// 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);
}
// 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));
}
}
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));
static advice(StandardSession s): invalidate(s) {
before {
if (!s.isValid())
throw new IllegalStateException
(s.sm.getString("standardSession."
+ thisJoinPoint.methodName
+ ".ise"));
}
}
}
// -------------------------------------------------------------- Private Class
/**
* This class is a dummy implementation of the <code>HttpSessionContext</code>
* interface, to conform to the requirement that such an object be returned
* when <code>HttpSession.getSessionContext()</code> 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.
*/
final class StandardSessionContext implements HttpSessionContext {
private Vector dummy = new Vector();
/**
* Return the session identifiers of all sessions defined
* within this context.
*
* @deprecated As of Java Servlet API 2.1 with no replacement.
* This method must return an empty <code>Enumeration</code>
* and will be removed in a future version of the API.
*/
public Enumeration getIds() {
return (dummy.elements());
}
/**
* Return the <code>HttpSession</code> associated with the
* specified session identifier.
*
* @param id Session identifier for which to look up a session
*
* @deprecated As of Java Servlet API 2.1 with no replacement.
* This method must return null and will be removed in a
* future version of the API.
*/
public HttpSession getSession(String id) {
return (null);
}
}
StandardSession
package org.apache.tomcat.session;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tomcat.catalina.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.apache.tomcat.util.StringManager;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
/**
* Standard implementation of the <b>Manager</b> interface that provides
* no session persistence or distributable capabilities, but does support
* an optional, configurable, maximum number of active sessions allowed.
* <p>
* Lifecycle configuration of this component assumes an XML node
* in the following format:
* <code>
* <Manager className="org.apache.tomcat.session.StandardManager"
* checkInterval="60" maxActiveSessions="-1"
* maxInactiveInterval="-1" />
* </code>
* where you can adjust the following parameters, with default values
* in square brackets:
* <ul>
* <li><b>checkInterval</b> - The interval (in seconds) between background
* thread checks for expired sessions. [60]
* <li><b>maxActiveSessions</b> - The maximum number of sessions allowed to
* be active at once, or -1 for no limit. [-1]
* <li><b>maxInactiveInterval</b> - The default maximum number of seconds of
* inactivity before which the servlet container is allowed to time out
* a session, or -1 for no limit. This value should be overridden from
* the default session timeout specified in the web application deployment
* descriptor, if any. [-1]
* </ul>
*
* @author Craig R. McClanahan
* @version $Revision: 1.1.1.1 $ $Date: 2000/05/02 21:28:30 $
*/
public final class StandardManager
extends ManagerBase
implements Lifecycle, Runnable {
// ----------------------------------------------------- Instance Variables
/**
* The interval (in seconds) between checks for expired sessions.
*/
private int checkInterval = 60;
/**
* Has this component been configured yet?
*/
private boolean configured = false;
/**
* The descriptive information about this implementation.
*/
private static final String info = "StandardManager/1.0";
/**
* The maximum number of active Sessions allowed, or -1 for no limit.
*/
protected int maxActiveSessions = -1;
/**
* The string manager for this package.
*/
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
/**
* Has this component been started yet?
*/
private boolean started = false;
/**
* The background thread.
*/
private Thread thread = null;
/**
* The background thread completion semaphore.
*/
private boolean threadDone = false;
/**
* Name to register for the background thread.
*/
private String threadName = "StandardManager";
// ------------------------------------------------------------- Properties
/**
* Return the check interval (in seconds) for this Manager.
*/
public int getCheckInterval() {
return (this.checkInterval);
}
/**
* Set the check interval (in seconds) for this Manager.
*
* @param checkInterval The new check interval
*/
public void setCheckInterval(int checkInterval) {
this.checkInterval = checkInterval;
}
/**
* Return descriptive information about this Manager implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo() {
return (this.info);
}
/**
* Return the maximum number of active Sessions allowed, or -1 for
* no limit.
*/
public int getMaxActiveSessions() {
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) {
this.maxActiveSessions = max;
}
// --------------------------------------------------------- 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 <code>null</code>.
*
* @exception IllegalStateException if a new session cannot be
* instantiated for any reason
*/
public Session createSession() {
if ((maxActiveSessions >= 0) &&
(sessions.size() >= maxActiveSessions))
throw new IllegalStateException
(sm.getString("standardManager.createSession.ise"));
return (super.createSession());
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Configure this component, based on the specified configuration
* parameters. This method should be called immediately after the
* component instance is created, and before <code>start()</code>
* is called.
*
* @param parameters Configuration parameters for this component
* (<B>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 {
// Validate and update our current component state
if (configured)
throw new LifecycleException
(sm.getString("standardManager.alreadyConfigured"));
configured = true;
if (parameters == null)
return;
// Parse and process our configuration parameters
if (!("Manager".equals(parameters.getNodeName())))
return;
NamedNodeMap attributes = parameters.getAttributes();
Node node = null;
node = attributes.getNamedItem("checkInterval");
if (node != null) {
try {
setCheckInterval(Integer.parseInt(node.getNodeValue()));
} catch (Throwable t) {
; // XXX - Throw exception?
}
}
node = attributes.getNamedItem("maxActiveSessions");
if (node != null) {
try {
setMaxActiveSessions(Integer.parseInt(node.getNodeValue()));
} catch (Throwable t) {
; // XXX - Throw exception?
}
}
node = attributes.getNamedItem("maxInactiveInterval");
if (node != null) {
try {
setMaxInactiveInterval(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 <code>configure()</code>,
* 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 {
// 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;
// Start the background reaper thread
threadStart();
}
/**
* 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 {
// Validate and update our current component state
if (!started)
throw new LifecycleException
(sm.getString("standardManager.notStarted"));
started = false;
// 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();
}
}
// -------------------------------------------------------- Private Methods
/**
* Sleep for the duration specified by the <code>checkInterval</code>
* property.
*/
private void threadSleep() {
try {
Thread.sleep(checkInterval * 1000L);
} catch (InterruptedException e) {
;
}
}
/**
* 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();
}
/**
* Stop the background thread that is periodically checking for
* session timeouts.
*/
private void threadStop() {
if (thread == null)
return;
threadDone = true;
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
;
}
thread = null;
}
// ------------------------------------------------------ Background Thread
/**
* The background thread that checks for session timeouts and shutdown.
*/
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}
}
StandardManager StandardSessionManager
package org.apache.tomcat.session;
import java.io.IOException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import org.apache.tomcat.catalina.*;
import org.apache.tomcat.core.Context;
import org.apache.tomcat.core.Request;
import org.apache.tomcat.core.Response;
import org.apache.tomcat.core.SessionManager;
import org.apache.tomcat.util.SessionUtil;
/**
* Specialized implementation of org.apache.tomcat.core.SessionManager
* that adapts to the new component-based Manager implementation.
* <p>
* XXX - At present, use of <code>StandardManager</code> is hard coded,
* and lifecycle configuration is not supported.
* <p>
* <b>IMPLEMENTATION NOTE</b>: Once we commit to the new Manager/Session
* paradigm, I would suggest moving the logic implemented here back into
* the core level. The Tomcat.Next "Manager" interface acts more like a
* collection class, and has minimal knowledge of the detailed request
* processing semantics of handling sessions.
* <p>
* 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
* timeout for this web application (specified in the deployment descriptor)
* should be.
*
* @author Craig R. McClanahan
*/
public final class StandardSessionManager
implements SessionManager {
// -----------------------------------------------------------
Constructors
/**
* Create a new SessionManager that adapts to the corresponding Manager
* implementation.
*/
public StandardSessionManager() {
manager = new StandardManager();
if (manager instanceof Lifecycle) {
try {
((Lifecycle) manager).configure(null);
((Lifecycle) manager).start();
} catch (LifecycleException e) {
throw new IllegalStateException("" + e);
}
}
}
// ----------------------------------------------------- Instance
Variables
/**
* The Manager implementation we are actually using.
*/
private Manager manager = null;
// --------------------------------------------------------- Public Methods
// XXX should we throw exception or just return null ??
public HttpSession findSession( Context ctx, String id ) {
try {
Session session = manager.findSession(id);
if(session!=null)
return session.getSession();
} catch (IOException e) {
}
return (null);
}
public HttpSession createSession(Context ctx) {
return manager.createSession().getSession();
}
/**
* Remove all sessions because our associated Context is being shut
down.
*
* @param ctx The context that is being shut down
*/
public void removeSessions(Context ctx) {
// XXX XXX a manager may be shared by multiple
// contexts, we just want to remove the sessions of ctx!
// The manager will still run after that ( i.e. keep database
// connection open
if (manager instanceof Lifecycle) {
try {
((Lifecycle) manager).stop();
} 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.
*
* @param minutes The session inactivity timeout in minutes.
*/
public void setSessionTimeOut(int minutes) {
if(-1 != minutes) {
// The manager works with seconds...
manager.setMaxInactiveInterval(minutes * 60);
}
}
}
ServerSessionManager
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 [duncan@eng.sun.com]
* @author Jason Hunter [jch@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
*/
public class ServerSessionManager implements SessionManager {
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
private static ServerSessionManager manager; // = new ServerSessionManager();
protected int inactiveInterval = -1;
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 HttpSession createSession(Context ctx) {
String sessionId = SessionIdGenerator.generateId();
ServerSession session = new ServerSession(sessionId);
sessions.put(sessionId, session);
if(-1 != inactiveInterval) {
session.setMaxInactiveInterval(inactiveInterval);
}
return session.getApplicationSession( ctx, true );
}
public HttpSession findSession(Context ctx, String id) {
ServerSession sSession=(ServerSession)sessions.get(id);
if(sSession==null) return null;
return sSession.getApplicationSession(ctx, false);
}
// 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.
synchronized void reap() {
Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ServerSession session = (ServerSession)sessions.get(key);
session.reap();
session.validate();
}
}
synchronized void removeSession(ServerSession session) {
String id = session.getId();
session.invalidate();
sessions.remove(id);
}
public void removeSessions(Context context) {
Enumeration enum = sessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ServerSession session = (ServerSession)sessions.get(key);
ApplicationSession appSession =
session.getApplicationSession(context, false);
if (appSession != null) {
appSession.invalidate();
}
}
}
/**
* 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.
*
* @param minutes The session inactivity timeout in minutes.
*/
public void setSessionTimeOut(int minutes) {
if(-1 != minutes) {
// The manager works with seconds...
inactiveInterval = (minutes * 60);
}
}
}
SessionInterceptor
package org.apache.tomcat.request;
import org.apache.tomcat.core.*;
import org.apache.tomcat.util.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.servlet.http.*;
/**
* Will process the request and determine the session Id, and set it
* in the Request.
* It also marks the session as accessed.
*
* This implementation only handles Cookies sessions, please extend or
* add new interceptors for other methods.
*
*/
public class SessionInterceptor extends BaseInterceptor implements RequestInterceptor {
// GS, separates the session id from the jvm route
static final char SESSIONID_ROUTE_SEP = '.';
int debug=0;
ContextManager cm;
public SessionInterceptor() {
}
public void setDebug( int i ) {
System.out.println("Set debug to " + i);
debug=i;
}
public void setContextManager( ContextManager cm ) {
this.cm=cm;
}
public int requestMap(Request request ) {
String sessionId = null;
Cookie cookies[]=request.getCookies(); // assert !=null
for( int i=0; i<cookies.length; i++ ) {
Cookie cookie = cookies[i];
if (cookie.getName().equals("JSESSIONID")) {
sessionId = cookie.getValue();
sessionId=validateSessionId(request, sessionId);
if (sessionId!=null){
request.setRequestedSessionIdFromCookie(true);
}
}
}
String sig=";jsessionid=";
int foundAt=-1;
if( debug>0 ) cm.log(" XXX RURI=" + request.getRequestURI());
if ((foundAt=request.getRequestURI().indexOf(sig))!=-1){
sessionId=request.getRequestURI().substring(foundAt+sig.length());
// rewrite URL, do I need to do anything more?
request.setRequestURI(request.getRequestURI().substring(0, foundAt));
sessionId=validateSessionId(request, sessionId);
if (sessionId!=null){
request.setRequestedSessionIdFromURL(true);
}
}
return 0;
}
// XXX what is the correct behavior if the session is invalid ?
// We may still set it and just return session invalid.
/** Validate and fix the session id. If the session is not valid return null.
* It will also clean up the session from load-balancing strings.
* @return sessionId, or null if not valid
*/
private String validateSessionId(Request request, String sessionId){
// GS, We piggyback the JVM id on top of the session cookie
// Separate them ...
if( debug>0 ) cm.log(" Orig sessionId " + sessionId );
if (null != sessionId) {
int idex = sessionId.lastIndexOf(SESSIONID_ROUTE_SEP);
if(idex > 0) {
sessionId = sessionId.substring(0, idex);
}
}
if (sessionId != null && sessionId.length()!=0) {
// GS, We are in a problem here, we may actually get
// multiple Session cookies (one for the root
// context and one for the real context... or old session
// cookie. We must check for validity in the current context.
Context ctx=request.getContext();
SessionManager sM = ctx.getSessionManager();
if(null != sM.findSession(ctx, sessionId)) {
request.setRequestedSessionId(sessionId);
if( debug>0 ) cm.log(" Final session id " + sessionId );
return sessionId;
}
}
return null;
}
public int beforeBody( Request rrequest, Response response ) {
String reqSessionId = response.getSessionId();
if( debug>0 ) cm.log("Before Body " + reqSessionId );
if( reqSessionId==null)
return 0;
// GS, set the path attribute to the cookie. This way
// multiple session cookies can be used, one for each
// context.
String sessionPath = rrequest.getContext().getPath();
if(sessionPath.length() == 0) {
sessionPath = "/";
}
// GS, piggyback the jvm route on the session id.
if(!sessionPath.equals("/")) {
String jvmRoute = rrequest.getJvmRoute();
if(null != jvmRoute) {
reqSessionId = reqSessionId + SESSIONID_ROUTE_SEP + jvmRoute;
}
}
Cookie cookie = new Cookie("JSESSIONID",
reqSessionId);
cookie.setMaxAge(-1);
cookie.setPath(sessionPath);
cookie.setVersion(1);
response.addHeader( CookieTools.getCookieHeaderName(cookie),
CookieTools.getCookieHeaderValue(cookie));
cookie.setVersion(0);
response.addHeader( CookieTools.getCookieHeaderName(cookie),
CookieTools.getCookieHeaderValue(cookie));
return 0;
}
/** Notification of context shutdown
*/
public void contextShutdown( Context ctx )
throws TomcatException
{
if( ctx.getDebug() > 0 ) ctx.log("Removing sessions from " + ctx );
ctx.getSessionManager().removeSessions(ctx);
}
}
ServerSession
package org.apache.tomcat.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.*;
/**
* Core implementation of a server session
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
*/
public class ServerSession {
private StringManager sm =
StringManager.getManager("org.apache.tomcat.session");
private Hashtable values = new Hashtable();
private Hashtable appSessions = new Hashtable();
private String id;
private long creationTime = System.currentTimeMillis();;
private long thisAccessTime = creationTime;
private int inactiveInterval = -1;
ServerSession(String id) {
this.id = id;
}
public String getId() {
return id;
}
public long getCreationTime() {
return creationTime;
}
public ApplicationSession getApplicationSession(Context context,
boolean create) {
ApplicationSession appSession =
(ApplicationSession)appSessions.get(context);
if (appSession == null && create) {
// XXX
// sync to ensure valid?
appSession = new ApplicationSession(id, this, context);
appSessions.put(context, appSession);
}
// XXX
// make sure that we haven't gone over the end of our
// inactive interval -- if so, invalidate and create
// a new appSession
return appSession;
}
void removeApplicationSession(Context context) {
appSessions.remove(context);
}
/**
* Called by context when request comes in so that accesses and
* inactivities can be dealt with accordingly.
*/
void validate()
synchronized void invalidate() {
Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ApplicationSession appSession =
(ApplicationSession)appSessions.get(key);
appSession.invalidate();
}
}
public void putValue(String name, Object value) {
if (name == null) {
String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg);
}
removeValue(name); // remove any existing binding
values.put(name, value);
}
public Object getValue(String name) {
if (name == null) {
String msg = sm.getString("serverSession.value.iae");
throw new IllegalArgumentException(msg);
}
return values.get(name);
}
public Enumeration getValueNames() {
return values.keys();
}
public void removeValue(String name) {
values.remove(name);
}
public void setMaxInactiveInterval(int interval) {
inactiveInterval = interval;
}
public int getMaxInactiveInterval() {
return inactiveInterval;
}
// 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.
synchronized void reap() {
Enumeration enum = appSessions.keys();
while (enum.hasMoreElements()) {
Object key = enum.nextElement();
ApplicationSession appSession =
(ApplicationSession)appSessions.get(key);
appSession.validate();
}
}
}
private long lastAccessed = creationTime;
private int inactiveInterval = -1;
void accessed() {
// set last accessed to thisAccessTime as it will be left over
// from the previous access
lastAccessed = thisAccessTime;
thisAccessTime = System.currentTimeMillis();
validate();
}
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;
if (thisInterval > inactiveInterval) {
invalidate();
}
}
}
public long getLastAccessedTime() {
if (valid) {
return lastAccessed;
} else {
String msg = sm.getString("applicationSession.session.ise");
throw new IllegalStateException(msg);
}
}
public long getLastAccessedTime() {
return lastAccessed;
}
private long lastAccessed = creationTime;
void accessed() {
// set last accessed to thisAccessTime as it will be left over
// from the previous access
lastAccessed = thisAccessTime;
thisAccessTime = System.currentTimeMillis();
}
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;
if (thisInterval > inactiveInterval) {
invalidate();
ServerSessionManager ssm =
ServerSessionManager.getManager();
ssm.removeSession(this);
}
}
}
private long lastAccessedTime = creationTime;
/**
* 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 (this.lastAccessedTime);
}
this.lastAccessedTime = time;
/**
* 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() {
this.lastAccessedTime = this.thisAccessedTime;
this.thisAccessedTime = System.currentTimeMillis();
this.isNew=false;
}
lastAccessedTime = 0L;
lastAccessedTime = ((Long) stream.readObject()).longValue();
maxInactiveInterval = ((Integer)
stream.readObject()).intValue();
isNew = ((Boolean) stream.readObject()).booleanValue();
stream.writeObject(new Long(lastAccessedTime));
sM.accessed(ctx, request, sessionId );
public void accessed( Context ctx, Request req, String id ) {
ApplicationSession apS=(ApplicationSession)findSession( ctx, id);
if( apS==null) return;
ServerSession servS=apS.getServerSession();
servS.accessed();
apS.accessed();
// cache it - no need to compute it again
req.setSession( apS );
}
/**
* Invalidate all sessions that have expired.
*/
private void processExpires() {
long timeNow = System.currentTimeMillis();
Session sessions[] = findSessions();
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();
}
}
/**
* Mark the specified session's last accessed time. This should be
* called for each request by a RequestInterceptor.
*
* @param session The session to be marked
*/
public void accessed(Context ctx, Request req, String id) {
HttpSession session=findSession(ctx, id);
if( session == null) return;
if (session instanceof Session)
((Session) session).access();
// cache the HttpSession - avoid another find
req.setSession( session );
}
CASCON '04
12
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
–
aspectj-users@eclipse.org
CASCON '04
13
•
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
AspectJ applied to a large
middleware system
existing policy implementations
•
affect every file
–
5-30 page policy documents
–
applied by developers
•
affect every developer
–
must understand policy document
•
repeat for new code assets
•
awkward to support variants
–
complicates product line
•
don’t even think about
changing the policy
policies implemented with AspectJ
•
one reusable crosscutting module
–
policy captured explicitly
–
applies policy uniformly for all time
•
written by central team
–
no burden on other 492 developers
•
automatically applied to new code
•
easy plug and unplug
–
simplifies product line issues
•
changes to policy happen in one
place
剩余63页未读,继续阅读
2014-08-10 上传
2010-04-07 上传
2008-11-19 上传
2010-01-10 上传
2010-01-10 上传
2007-10-30 上传
2023-06-10 上传
wujie315997397
- 粉丝: 1
- 资源: 12
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- Angular实现MarcHayek简历展示应用教程
- Crossbow Spot最新更新 - 获取Chrome扩展新闻
- 量子管道网络优化与Python实现
- Debian系统中APT缓存维护工具的使用方法与实践
- Python模块AccessControl的Windows64位安装文件介绍
- 掌握最新*** Fisher资讯,使用Google Chrome扩展
- Ember应用程序开发流程与环境配置指南
- EZPCOpenSDK_v5.1.2_build***版本更新详情
- Postcode-Finder:利用JavaScript和Google Geocode API实现
- AWS商业交易监控器:航线行为分析与营销策略制定
- AccessControl-4.0b6压缩包详细使用教程
- Python编程实践与技巧汇总
- 使用Sikuli和Python打造颜色求解器项目
- .Net基础视频教程:掌握GDI绘图技术
- 深入理解数据结构与JavaScript实践项目
- 双子座在线裁判系统:提高编程竞赛效率
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功