/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package org.glassfish.web.ha.session.management; import com.sun.enterprise.container.common.spi.util.JavaEEIOUtils; import com.sun.enterprise.web.ServerConfigLookup; import org.glassfish.ha.store.api.BackingStore; import org.glassfish.ha.store.api.BackingStoreException; import org.apache.catalina.*; import org.apache.catalina.session.*; import java.io.*; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.logging.Level; /** * * @author Larry White * @author Rajiv Mordani */ public class ReplicationAttributeStore extends ReplicationStore { /** Creates a new instance of ReplicationAttributeStore */ public ReplicationAttributeStore(JavaEEIOUtils ioUtils) { super(ioUtils); setLogLevel(); } // HAStorePoolElement methods begin /** * Save the specified Session into this Store. Any previously saved * information for the associated session identifier is replaced. * * @param session Session to be saved * * @exception IOException if an input/output error occurs */ public void valveSave(Session session) throws IOException { if (!(session instanceof HASession)) { return; } HASession haSess = (HASession)session; if( haSess.isPersistent() && !haSess.isDirty() ) { this.updateLastAccessTime(session); } else { this.doValveSave(session); haSess.setPersistent(true); } haSess.setDirty(false); } // Store method begin /** * Save the specified Session into this Store. Any previously saved * information for the associated session identifier is replaced. * * @param session Session to be saved * * @exception IOException if an input/output error occurs */ public void save(Session session) throws IOException { if (!(session instanceof HASession)) { return; } HASession haSess = (HASession)session; if( haSess.isPersistent() && !haSess.isDirty() ) { this.updateLastAccessTime(session); } else { this.doSave(session); haSess.setPersistent(true); } haSess.setDirty(false); } /** * Save the specified Session into this Store. Any previously saved * information for the associated session identifier is replaced. * * @param session Session to be saved * * @exception IOException if an input/output error occurs */ @Override public void doValveSave(Session session) throws IOException { if(_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>doValveSave:valid =" + ((StandardSession)session).getIsValid()); if (session instanceof HASession) { _logger.fine("ReplicationAttributeStore>>valveSave:ssoId=" + ((HASession)session).getSsoId()); } } // begin 6470831 do not save if session is not valid if( !((StandardSession)session).getIsValid() ) { return; } // end 6470831 if (!(session instanceof ModifiedAttributeHASession) || !(session instanceof BaseHASession)) { return; } ModifiedAttributeHASession modAttrSession = (ModifiedAttributeHASession)session; String userName = ""; if(session.getPrincipal() !=null){ userName = session.getPrincipal().getName(); ((BaseHASession)session).setUserName(userName); } BackingStore<String, CompositeMetadata> replicator = getCompositeMetadataBackingStore(); if(_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>save: replicator: " + replicator); } CompositeMetadata compositeMetadata = createCompositeMetadata(modAttrSession); try { if (_logger.isLoggable(Level.FINE)) { _logger.fine("CompositeMetadata is " + compositeMetadata + " id is " + session.getIdInternal()); } replicator.save(session.getIdInternal(), //id compositeMetadata, !((HASession) session).isPersistent()); modAttrSession.resetAttributeState(); postSaveUpdate(modAttrSession); } catch (BackingStoreException ex) { //FIXME } } /** * Save the specified Session into this Store. Any previously saved * information for the associated session identifier is replaced. * * @param session Session to be saved * * @exception IOException if an input/output error occurs */ @Override public void doSave(Session session) throws IOException { // begin 6470831 do not save if session is not valid if( !((StandardSession)session).getIsValid() ) { return; } if (!(session instanceof ModifiedAttributeHASession) || !(session instanceof HASession)) { return; } // end 6470831 ModifiedAttributeHASession modAttrSession = (ModifiedAttributeHASession)session; BackingStore<String, CompositeMetadata> replicator = getCompositeMetadataBackingStore(); if(_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>doSave: replicator: " + replicator); } CompositeMetadata compositeMetadata = createCompositeMetadata(modAttrSession); try { if (_logger.isLoggable(Level.FINE)) { _logger.fine("CompositeMetadata is " + compositeMetadata + " id is " + session.getIdInternal()); } replicator.save(session.getIdInternal(), //id compositeMetadata, !((HASession) session).isPersistent()); modAttrSession.resetAttributeState(); postSaveUpdate(modAttrSession); } catch (BackingStoreException ex) { //FIXME } } @SuppressWarnings("unchecked") private BackingStore<String, CompositeMetadata> getCompositeMetadataBackingStore() { ReplicationManagerBase<CompositeMetadata> mgr = (ReplicationManagerBase<CompositeMetadata>) this.getManager(); return mgr.getBackingStore(); } @Override public Session load(String id, String version) throws ClassNotFoundException, IOException { try { CompositeMetadata metaData = getCompositeMetadataBackingStore().load(id, version); if(_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>load:id=" + id + ", metaData=" + metaData); } Session session = getSession(metaData); validateAndSave(session); return session; } catch (BackingStoreException ex) { IOException ex1 = (IOException) new IOException("Error during load: " + ex.getMessage()).initCause(ex); throw ex1; } } private void validateAndSave(Session session) throws IOException { if (session != null) { if (_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>validateAndSave saving " + "the session after loading it. Session=" + session); } //save session - save will reset dirty to false ((HASession) session).setDirty(true); valveSave(session); // TODO :: revisit this for third party backing stores; } if (session != null) { ((HASession) session).setDirty(false); ((HASession) session).setPersistent(false); } } public Session getSession(CompositeMetadata metadata) throws IOException { if (metadata == null || metadata.getState() == null) { return null; } byte[] state = metadata.getState(); Session _session = null; BufferedInputStream bis = null; ByteArrayInputStream bais = null; Loader loader = null; ClassLoader classLoader = null; ObjectInputStream ois = null; Container container = manager.getContainer(); java.security.Principal pal=null; //MERGE chg added String ssoId = null; long version = 0L; try { bais = new ByteArrayInputStream(state); bis = new BufferedInputStream(bais); //Get the username, ssoId from metadata //ssoId = metadata.getSsoId(); ssoId = metadata.getStringExtraParam(); version = metadata.getVersion(); //debug("ReplicationStore.getSession() id="+id+" username ="+username+";"); if(_logger.isLoggable(Level.FINEST)) { _logger.finest("loaded session from replicationstore, length = "+state.length); } if (container != null) { loader = container.getLoader(); } if (loader != null) { classLoader = loader.getClassLoader(); } if (classLoader != null) { try { ois = ioUtils.createObjectInputStream(bis, true, classLoader); } catch (Exception ex) {} } if (ois == null) { ois = new ObjectInputStream(bis); } if(ois != null) { try { _session = readSession(manager, ois); } finally { try { ois.close(); bis = null; } catch (IOException e) { } } } } catch(ClassNotFoundException e) { IOException ex1 = (IOException) new IOException( "Error during deserialization: " + e.getMessage()).initCause(e); throw ex1; } catch(IOException e) { throw e; } String username = ((HASession)_session).getUserName(); if((username !=null) && (!username.equals("")) && _session.getPrincipal() == null) { if (_debug > 0) { debug("Username retrieved is "+username); } pal = ((com.sun.web.security.RealmAdapter)container.getRealm()).createFailOveredPrincipal(username); if (_debug > 0) { debug("principal created using username "+pal); } if(pal != null) { _session.setPrincipal(pal); if (_debug > 0) { debug("getSession principal="+pal+" was added to session="+_session); } } } //--SRI _session.setNew(false); if(_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>ssoId=" + ssoId); } /* ((BaseHASession)_session).setSsoId(ssoId); if((ssoId !=null) && (!ssoId.equals(""))) associate(ssoId, _session); */ ((HASession)_session).setVersion(version); ((HASession)_session).setDirty(false); //now load entries from deserialized entries collection ((ModifiedAttributeHASession)_session).clearAttributeStates(); byte[] entriesState = metadata.getState(); Collection entries = null; if(entriesState != null) { entries = this.deserializeStatesCollection(entriesState); loadAttributes((ModifiedAttributeHASession)_session, entries); } loadAttributes((ModifiedAttributeHASession)_session, metadata.getEntries()); return _session; } //metadata related private void postSaveUpdate(ModifiedAttributeHASession modAttrSession) { if(_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>postSaveUpdate"); } List<String> addedAttrs = modAttrSession.getAddedAttributes(); List<String> modifiedAttrs = modAttrSession.getModifiedAttributes(); List<String> deletedAttrs = modAttrSession.getDeletedAttributes(); printAttrList("ADDED", addedAttrs); printAttrList("MODIFIED", modifiedAttrs); printAttrList("DELETED", deletedAttrs); postProcessSetAttrStates(modAttrSession, addedAttrs); postProcessSetAttrStates(modAttrSession, modifiedAttrs); } private void postProcessSetAttrStates(ModifiedAttributeHASession modAttrSession, List<String> attrsList) { for(int i=0; i<attrsList.size(); i++) { String nextStateName = attrsList.get(i); modAttrSession.setAttributeStatePersistent(nextStateName, true); modAttrSession.setAttributeStateDirty(nextStateName, false); } } private CompositeMetadata createCompositeMetadata(ModifiedAttributeHASession modAttrSession) { byte[] trunkState = null; if (modAttrSession.isNew()) { try { trunkState = this.getByteArray(modAttrSession); } catch(IOException ex) { //no op } } if(_logger.isLoggable(Level.FINE)) { _logger.fine("ReplicationAttributeStore>>createCompositeMetadata:trunkState=" + trunkState); } List<SessionAttributeMetadata> entries = new ArrayList<SessionAttributeMetadata>(); List<String> addedAttrs = modAttrSession.getAddedAttributes(); List<String> modifiedAttrs = modAttrSession.getModifiedAttributes(); List<String> deletedAttrs = modAttrSession.getDeletedAttributes(); printAttrList("ADDED", addedAttrs); printAttrList("MODIFIED", modifiedAttrs); printAttrList("DELETED", deletedAttrs); addToEntries(modAttrSession, entries, SessionAttributeMetadata.Operation.ADD, addedAttrs); addToEntries(modAttrSession, entries, SessionAttributeMetadata.Operation.UPDATE, modifiedAttrs); addToEntries(modAttrSession, entries, SessionAttributeMetadata.Operation.DELETE, deletedAttrs); CompositeMetadata result = new CompositeMetadata(modAttrSession.getVersion(), modAttrSession.getLastAccessedTimeInternal(), modAttrSession.getMaxInactiveInterval()*1000L, entries, trunkState, null); return result; } private void printAttrList(String attrListType, List<String> attrList) { if (_logger.isLoggable(Level.FINE)) { _logger.fine("AttributeType = " + attrListType); String nextAttrName = null; for(int i=0; i<attrList.size(); i++) { nextAttrName = attrList.get(i); _logger.fine("attribute[" + i + "]=" + nextAttrName); } } } private void addToEntries(ModifiedAttributeHASession modAttrSession, List<SessionAttributeMetadata> entries, SessionAttributeMetadata.Operation op, List<String> attrList) { String nextAttrName = null; Object nextAttrValue = null; byte[] nextValue = null; for(int i=0; i<attrList.size(); i++) { nextAttrName = attrList.get(i); nextAttrValue = ((StandardSession) modAttrSession).getAttribute(nextAttrName); nextValue = null; try { nextValue = getByteArray(nextAttrValue); } catch (IOException ex) {} SessionAttributeMetadata nextAttrMetadata = new SessionAttributeMetadata(nextAttrName, op, nextValue); entries.add(nextAttrMetadata); } } /** * Create an byte[] for the session that we can then pass to * the HA Store. * * @param attributeValue * The attribute value we are serializing * */ protected byte[] getByteArray(Object attributeValue) throws IOException { ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; byte[] obs; try { bos = new ByteArrayOutputStream(); try { oos = ioUtils.createObjectOutputStream(new BufferedOutputStream(bos), true); } catch (Exception ex) {} //use normal ObjectOutputStream if there is a failure during stream creation if(oos == null) { oos = new ObjectOutputStream(new BufferedOutputStream(bos)); } oos.writeObject(attributeValue); oos.close(); oos = null; obs = bos.toByteArray(); } finally { if ( oos != null ) { oos.close(); } } return obs; } /** * Given a byte[] containing session data, return a session * object * * @param state * The byte[] with the session attribute data * * @return * A newly created object for the given session attribute data */ protected Object getAttributeValue(byte[] state) throws IOException, ClassNotFoundException { Object attributeValue = null; BufferedInputStream bis = null; ByteArrayInputStream bais = null; Loader loader = null; ClassLoader classLoader = null; ObjectInputStream ois = null; Container container = manager.getContainer(); try { bais = new ByteArrayInputStream(state); bis = new BufferedInputStream(bais); if (container != null) { loader = container.getLoader(); } if (loader != null) { classLoader = loader.getClassLoader(); } if (classLoader != null) { try { ois = ioUtils.createObjectInputStream(bis, true, classLoader); } catch (Exception ex) {} } if (ois == null) { ois = new ObjectInputStream(bis); } if(ois != null) { try { attributeValue = ois.readObject(); } finally { try { ois.close(); bis = null; } catch (IOException e) { } } } } catch(ClassNotFoundException e) { if (_logger.isLoggable(Level.FINE)) { _logger.log(Level.FINE, "ClassNotFoundException occurred in getAttributeValue", e); } throw e; } catch(IOException e) { throw e; } return attributeValue; } //new serialization code for Collection /** * Create an byte[] for the session that we can then pass to * the HA Store. * * @param entries * The Collection of entries we are serializing * */ protected byte[] getByteArrayFromCollection(Collection entries) throws IOException { ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; byte[] obs; try { bos = new ByteArrayOutputStream(); try { oos = ioUtils.createObjectOutputStream(new BufferedOutputStream(bos), true); } catch (Exception ex) {} //use normal ObjectOutputStream if there is a failure during stream creation if(oos == null) { oos = new ObjectOutputStream(new BufferedOutputStream(bos)); } //first write out the entriesSize int entriesSize = entries.size(); oos.writeObject(Integer.valueOf(entriesSize)); //then write out the entries Iterator it = entries.iterator(); while(it.hasNext()) { oos.writeObject(it.next()); } oos.close(); oos = null; obs = bos.toByteArray(); } finally { if ( oos != null ) { oos.close(); } } return obs; } /** * Given a byte[] containing session data, return a session * object * * @param state * The byte[] with the session attribute data * * @return * A newly created object for the given session attribute data */ protected Object getAttributeValueCollection(byte[] state) throws IOException, ClassNotFoundException { Collection<Object> attributeValueList = new ArrayList<Object>(); BufferedInputStream bis = null; ByteArrayInputStream bais = null; Loader loader = null; ClassLoader classLoader = null; ObjectInputStream ois = null; Container container = manager.getContainer(); try { bais = new ByteArrayInputStream(state); bis = new BufferedInputStream(bais); if (container != null) { loader = container.getLoader(); } if (loader != null) { classLoader = loader.getClassLoader(); } if (classLoader != null) { try { ois = ioUtils.createObjectInputStream(bis, true, classLoader); } catch (Exception ex) {} } if (ois == null) { ois = new ObjectInputStream(bis); } if(ois != null) { try { //first get List size Object whatIsIt = ois.readObject(); int entriesSize = 0; if(whatIsIt instanceof Integer) { entriesSize = ((Integer)whatIsIt).intValue(); } for (int i = 0; i < entriesSize; i++) { Object nextAttributeValue = ois.readObject(); attributeValueList.add(nextAttributeValue); } } finally { try { ois.close(); bis = null; } catch (IOException e) { } } } } catch(ClassNotFoundException e) { if (_logger.isLoggable(Level.FINE)) { _logger.log(Level.FINE, "ClassNotFoundException occurred in getAttributeValueCollection", e); } throw e; } catch(IOException e) { throw e; } return attributeValueList; } //end new serialization code for Collection /** * Given a session, load its attributes * * @param modifiedAttributeSession * The session (header info only) having its attributes loaded * * @param attributeList * The List<AttributeMetadata> list of loaded attributes * * @return * A newly created object for the given session attribute data */ protected void loadAttributes(ModifiedAttributeHASession modifiedAttributeSession, Collection attributeList) { if(_logger.isLoggable(Level.FINEST)) { _logger.finest("in loadAttributes -- ReplicationAttributeStore : session id=" + modifiedAttributeSession.getIdInternal()); } String thisAttrName = null; //SessionAttributeMetadata.Operation thisAttrOp = null; Object thisAttrVal = null; Iterator it = attributeList.iterator(); while (it.hasNext()) { SessionAttributeMetadata nextAttrMetadata = (SessionAttributeMetadata)it.next(); thisAttrName = nextAttrMetadata.getAttributeName(); //thisAttrOp = nextAttrMetadata.getOperation(); byte[] nextAttrState = nextAttrMetadata.getState(); thisAttrVal = null; try { thisAttrVal = getAttributeValue(nextAttrState); } catch (ClassNotFoundException ex1) { //FIXME log? } catch (IOException ex2) {} if(_logger.isLoggable(Level.FINEST)) { _logger.finest("Attr retrieved======" + thisAttrName); } if(thisAttrVal != null) { //start if if(_logger.isLoggable(Level.FINEST)) { _logger.finest("Setting Attribute: " + thisAttrName); } modifiedAttributeSession.setAttribute(thisAttrName, thisAttrVal); modifiedAttributeSession.setAttributeStatePersistent(thisAttrName, false); modifiedAttributeSession.setAttributeStateDirty(thisAttrName, false); } //end if } //end while } /* private byte[] serializeStatesCollection(Collection entries) { byte[] result = null; try { result = getByteArrayFromCollection(entries); } catch (IOException ex) {} return result; } private byte[] serializeStatesCollectionPrevious(Collection entries) { byte[] result = null; try { result = getByteArray(entries); } catch (IOException ex) {} return result; } */ private Collection deserializeStatesCollection(byte[] entriesState) { Collection result = new ArrayList(); try { result = (Collection)getAttributeValueCollection(entriesState); } catch (ClassNotFoundException ex1) { // FIXME log? } catch (IOException ex2) {} return result; } /* private Collection deserializeStatesCollectionPrevious(byte[] entriesState) { Collection result = new ArrayList(); try { result = (Collection)getAttributeValue(entriesState); } catch (ClassNotFoundException ex1) { // FIXME log? } catch (IOException ex2) {} return result; } */ }