| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.catalina.ha.session; |
| |
| /** |
| * This class is used to track the series of actions that happens when |
| * a request is executed. These actions will then translate into invocations of methods |
| * on the actual session. |
| * This class is NOT thread safe. One DeltaRequest per session |
| * @version 1.0 |
| */ |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.Externalizable; |
| import java.io.IOException; |
| import java.io.ObjectOutputStream; |
| import java.security.Principal; |
| import java.util.LinkedList; |
| |
| import org.apache.catalina.SessionListener; |
| import org.apache.catalina.realm.GenericPrincipal; |
| import org.apache.juli.logging.Log; |
| import org.apache.juli.logging.LogFactory; |
| import org.apache.tomcat.util.res.StringManager; |
| |
| |
| public class DeltaRequest implements Externalizable { |
| |
| public static final Log log = LogFactory.getLog(DeltaRequest.class); |
| |
| /** |
| * The string manager for this package. |
| */ |
| protected static final StringManager sm = StringManager.getManager(DeltaRequest.class); |
| |
| public static final int TYPE_ATTRIBUTE = 0; |
| public static final int TYPE_PRINCIPAL = 1; |
| public static final int TYPE_ISNEW = 2; |
| public static final int TYPE_MAXINTERVAL = 3; |
| public static final int TYPE_AUTHTYPE = 4; |
| public static final int TYPE_LISTENER = 5; |
| |
| public static final int ACTION_SET = 0; |
| public static final int ACTION_REMOVE = 1; |
| |
| public static final String NAME_PRINCIPAL = "__SET__PRINCIPAL__"; |
| public static final String NAME_MAXINTERVAL = "__SET__MAXINTERVAL__"; |
| public static final String NAME_ISNEW = "__SET__ISNEW__"; |
| public static final String NAME_AUTHTYPE = "__SET__AUTHTYPE__"; |
| public static final String NAME_LISTENER = "__SET__LISTENER__"; |
| |
| private String sessionId; |
| private LinkedList<AttributeInfo> actions = new LinkedList<>(); |
| private final LinkedList<AttributeInfo> actionPool = new LinkedList<>(); |
| |
| private boolean recordAllActions = false; |
| |
| public DeltaRequest() { |
| |
| } |
| |
| public DeltaRequest(String sessionId, boolean recordAllActions) { |
| this.recordAllActions=recordAllActions; |
| if(sessionId != null) |
| setSessionId(sessionId); |
| } |
| |
| |
| public void setAttribute(String name, Object value) { |
| int action = (value==null)?ACTION_REMOVE:ACTION_SET; |
| addAction(TYPE_ATTRIBUTE,action,name,value); |
| } |
| |
| public void removeAttribute(String name) { |
| int action = ACTION_REMOVE; |
| addAction(TYPE_ATTRIBUTE,action,name,null); |
| } |
| |
| public void setMaxInactiveInterval(int interval) { |
| int action = ACTION_SET; |
| addAction(TYPE_MAXINTERVAL,action,NAME_MAXINTERVAL,Integer.valueOf(interval)); |
| } |
| |
| /** |
| * convert principal at SerializablePrincipal for backup nodes. |
| * Only support principals from type {@link GenericPrincipal GenericPrincipal} |
| * @param p Session principal |
| * @see GenericPrincipal |
| */ |
| public void setPrincipal(Principal p) { |
| int action = (p==null)?ACTION_REMOVE:ACTION_SET; |
| GenericPrincipal gp = null; |
| if (p != null) { |
| if (p instanceof GenericPrincipal) { |
| gp = (GenericPrincipal) p; |
| if(log.isDebugEnabled()) |
| log.debug(sm.getString("deltaRequest.showPrincipal", p.getName() , getSessionId())); |
| } else |
| log.error(sm.getString("deltaRequest.wrongPrincipalClass",p.getClass().getName())); |
| } |
| addAction(TYPE_PRINCIPAL, action, NAME_PRINCIPAL, gp); |
| } |
| |
| public void setNew(boolean n) { |
| int action = ACTION_SET; |
| addAction(TYPE_ISNEW,action,NAME_ISNEW,Boolean.valueOf(n)); |
| } |
| |
| public void setAuthType(String authType) { |
| int action = (authType==null)?ACTION_REMOVE:ACTION_SET; |
| addAction(TYPE_AUTHTYPE,action,NAME_AUTHTYPE, authType); |
| } |
| |
| public void addSessionListener(SessionListener listener) { |
| addAction(TYPE_LISTENER, ACTION_SET, NAME_LISTENER ,listener); |
| } |
| |
| public void removeSessionListener(SessionListener listener) { |
| addAction(TYPE_LISTENER, ACTION_REMOVE, NAME_LISTENER ,listener); |
| } |
| |
| protected void addAction(int type, |
| int action, |
| String name, |
| Object value) { |
| AttributeInfo info = null; |
| if ( this.actionPool.size() > 0 ) { |
| try { |
| info = actionPool.removeFirst(); |
| }catch ( Exception x ) { |
| log.error("Unable to remove element:",x); |
| info = new AttributeInfo(type, action, name, value); |
| } |
| info.init(type,action,name,value); |
| } else { |
| info = new AttributeInfo(type, action, name, value); |
| } |
| //if we have already done something to this attribute, make sure |
| //we don't send multiple actions across the wire |
| if ( !recordAllActions) { |
| try { |
| actions.remove(info); |
| } catch (java.util.NoSuchElementException x) { |
| //do nothing, we wanted to remove it anyway |
| } |
| } |
| //add the action |
| actions.addLast(info); |
| } |
| |
| public void execute(DeltaSession session, boolean notifyListeners) { |
| if ( !this.sessionId.equals( session.getId() ) ) |
| throw new java.lang.IllegalArgumentException("Session id mismatch, not executing the delta request"); |
| session.access(); |
| for ( int i=0; i<actions.size(); i++ ) { |
| AttributeInfo info = actions.get(i); |
| switch ( info.getType() ) { |
| case TYPE_ATTRIBUTE: |
| if ( info.getAction() == ACTION_SET ) { |
| if ( log.isTraceEnabled() ) log.trace("Session.setAttribute('"+info.getName()+"', '"+info.getValue()+"')"); |
| session.setAttribute(info.getName(), info.getValue(),notifyListeners,false); |
| } else { |
| if ( log.isTraceEnabled() ) log.trace("Session.removeAttribute('"+info.getName()+"')"); |
| session.removeAttribute(info.getName(),notifyListeners,false); |
| } |
| |
| break; |
| case TYPE_ISNEW: |
| if ( log.isTraceEnabled() ) log.trace("Session.setNew('"+info.getValue()+"')"); |
| session.setNew(((Boolean)info.getValue()).booleanValue(),false); |
| break; |
| case TYPE_MAXINTERVAL: |
| if ( log.isTraceEnabled() ) log.trace("Session.setMaxInactiveInterval('"+info.getValue()+"')"); |
| session.setMaxInactiveInterval(((Integer)info.getValue()).intValue(),false); |
| break; |
| case TYPE_PRINCIPAL: |
| Principal p = null; |
| if (info.getAction() == ACTION_SET) { |
| p = (Principal) info.getValue(); |
| } |
| session.setPrincipal(p,false); |
| break; |
| case TYPE_AUTHTYPE: |
| String authType = null; |
| if ( info.getAction() == ACTION_SET ) { |
| authType = (String)info.getValue(); |
| } |
| session.setAuthType(authType,false); |
| break; |
| case TYPE_LISTENER: |
| SessionListener listener = (SessionListener) info.getValue(); |
| if (info.getAction() == ACTION_SET) { |
| session.addSessionListener(listener); |
| } else { |
| session.removeSessionListener(listener); |
| } |
| break; |
| default : |
| throw new java.lang.IllegalArgumentException("Invalid attribute info type="+info); |
| }//switch |
| }//for |
| session.endAccess(); |
| reset(); |
| } |
| |
| public void reset() { |
| while ( actions.size() > 0 ) { |
| try { |
| AttributeInfo info = actions.removeFirst(); |
| info.recycle(); |
| actionPool.addLast(info); |
| }catch ( Exception x ) { |
| log.error("Unable to remove element",x); |
| } |
| } |
| actions.clear(); |
| } |
| |
| public String getSessionId() { |
| return sessionId; |
| } |
| public void setSessionId(String sessionId) { |
| this.sessionId = sessionId; |
| if ( sessionId == null ) { |
| new Exception("Session Id is null for setSessionId").fillInStackTrace().printStackTrace(); |
| } |
| } |
| public int getSize() { |
| return actions.size(); |
| } |
| |
| public void clear() { |
| actions.clear(); |
| actionPool.clear(); |
| } |
| |
| @Override |
| public void readExternal(java.io.ObjectInput in) throws IOException,ClassNotFoundException { |
| //sessionId - String |
| //recordAll - boolean |
| //size - int |
| //AttributeInfo - in an array |
| reset(); |
| sessionId = in.readUTF(); |
| recordAllActions = in.readBoolean(); |
| int cnt = in.readInt(); |
| if (actions == null) |
| actions = new LinkedList<>(); |
| else |
| actions.clear(); |
| for (int i = 0; i < cnt; i++) { |
| AttributeInfo info = null; |
| if (this.actionPool.size() > 0) { |
| try { |
| info = actionPool.removeFirst(); |
| } catch ( Exception x ) { |
| log.error("Unable to remove element",x); |
| info = new AttributeInfo(); |
| } |
| } |
| else { |
| info = new AttributeInfo(); |
| } |
| info.readExternal(in); |
| actions.addLast(info); |
| }//for |
| } |
| |
| |
| @Override |
| public void writeExternal(java.io.ObjectOutput out ) throws java.io.IOException { |
| //sessionId - String |
| //recordAll - boolean |
| //size - int |
| //AttributeInfo - in an array |
| out.writeUTF(getSessionId()); |
| out.writeBoolean(recordAllActions); |
| out.writeInt(getSize()); |
| for ( int i=0; i<getSize(); i++ ) { |
| AttributeInfo info = actions.get(i); |
| info.writeExternal(out); |
| } |
| } |
| |
| /** |
| * serialize DeltaRequest |
| * @see DeltaRequest#writeExternal(java.io.ObjectOutput) |
| * |
| * @return serialized delta request |
| * @throws IOException |
| */ |
| protected byte[] serialize() throws IOException { |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| ObjectOutputStream oos = new ObjectOutputStream(bos); |
| writeExternal(oos); |
| oos.flush(); |
| oos.close(); |
| return bos.toByteArray(); |
| } |
| |
| private static class AttributeInfo implements java.io.Externalizable { |
| private String name = null; |
| private Object value = null; |
| private int action; |
| private int type; |
| |
| public AttributeInfo() { |
| this(-1, -1, null, null); |
| } |
| |
| public AttributeInfo(int type, |
| int action, |
| String name, |
| Object value) { |
| super(); |
| init(type,action,name,value); |
| } |
| |
| public void init(int type, |
| int action, |
| String name, |
| Object value) { |
| this.name = name; |
| this.value = value; |
| this.action = action; |
| this.type = type; |
| } |
| |
| public int getType() { |
| return type; |
| } |
| |
| public int getAction() { |
| return action; |
| } |
| |
| public Object getValue() { |
| return value; |
| } |
| @Override |
| public int hashCode() { |
| return name.hashCode(); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void recycle() { |
| name = null; |
| value = null; |
| type=-1; |
| action=-1; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if ( ! (o instanceof AttributeInfo ) ) return false; |
| AttributeInfo other = (AttributeInfo)o; |
| return other.getName().equals(this.getName()); |
| } |
| |
| @Override |
| public void readExternal(java.io.ObjectInput in ) throws IOException,ClassNotFoundException { |
| //type - int |
| //action - int |
| //name - String |
| //hasvalue - boolean |
| //value - object |
| type = in.readInt(); |
| action = in.readInt(); |
| name = in.readUTF(); |
| boolean hasValue = in.readBoolean(); |
| if ( hasValue ) value = in.readObject(); |
| } |
| |
| @Override |
| public void writeExternal(java.io.ObjectOutput out) throws IOException { |
| //type - int |
| //action - int |
| //name - String |
| //hasvalue - boolean |
| //value - object |
| out.writeInt(getType()); |
| out.writeInt(getAction()); |
| out.writeUTF(getName()); |
| out.writeBoolean(getValue()!=null); |
| if (getValue()!=null) out.writeObject(getValue()); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder buf = new StringBuilder("AttributeInfo[type="); |
| buf.append(getType()).append(", action=").append(getAction()); |
| buf.append(", name=").append(getName()).append(", value=").append(getValue()); |
| buf.append(", addr=").append(super.toString()).append("]"); |
| return buf.toString(); |
| } |
| |
| } |
| |
| } |