Coverage Report - org.kuali.rice.krad.web.session.NonSerializableSessionListener
 
Classes in this File Line Coverage Branch Coverage Complexity
NonSerializableSessionListener
65%
27/41
62%
10/16
2.375
 
 1  
 /**
 2  
  * Copyright 2005-2011 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.krad.web.session;
 17  
 
 18  
 import org.apache.commons.logging.Log;
 19  
 import org.apache.commons.logging.LogFactory;
 20  
 import org.kuali.rice.core.api.config.property.Config;
 21  
 import org.kuali.rice.core.api.config.property.ConfigContext;
 22  
 
 23  
 import javax.servlet.http.HttpSessionAttributeListener;
 24  
 import javax.servlet.http.HttpSessionBindingEvent;
 25  
 import java.io.ByteArrayOutputStream;
 26  
 import java.io.IOException;
 27  
 import java.io.ObjectOutputStream;
 28  
 import java.io.Serializable;
 29  
 
 30  
 //TODO: May want to have a way to turn this off that way we aren't incurring the overhead
 31  
 //of testing every session object if it is serializable by actually serializing it!
 32  
 
 33  
 /** A session listener that detects when a non-serializable attributes is added to session. **/
 34  2
 public class NonSerializableSessionListener implements HttpSessionAttributeListener {
 35  1
     private static final Log LOG = LogFactory.getLog(NonSerializableSessionListener.class);
 36  
 
 37  
     @Override
 38  
     public void attributeAdded(HttpSessionBindingEvent se) {
 39  2
         logSerializationViolations(se, "added");
 40  2
     }
 41  
 
 42  
     @Override
 43  
     public void attributeRemoved(HttpSessionBindingEvent se) {
 44  
         //do nothing
 45  0
     }
 46  
 
 47  
     @Override
 48  
     public void attributeReplaced(HttpSessionBindingEvent se) {
 49  0
         logSerializationViolations(se, "replaced");
 50  0
     }
 51  
 
 52  
     /**
 53  
      * Tests and logs serialization violations in non-production environments
 54  
      */
 55  
     private void logSerializationViolations(HttpSessionBindingEvent se, String action) {
 56  2
         if (!productionEnvironmentDetected()) {
 57  1
             checkSerialization(se, action);
 58  
         }
 59  2
     }
 60  
 
 61  
     /**
 62  
      * Determines whether we are running in a production environment.  Factored out for testability.
 63  
      */
 64  
     private static boolean productionEnvironmentDetected() {
 65  2
         Config c = ConfigContext.getCurrentContextConfig();
 66  2
         return c != null && c.isProductionEnvironment();
 67  
     }
 68  
 
 69  
     /**
 70  
      * Tests whether the attribute value is serializable and logs an error if it isn't.  Note, this can be expensive
 71  
      * so we avoid it in production environments.
 72  
      * @param se the session binding event
 73  
      * @param action the listener event for logging purposes (added or replaced)
 74  
      */
 75  
     protected void checkSerialization(final HttpSessionBindingEvent se, String action) {
 76  1
         final Object o = se.getValue();
 77  1
         if(o != null) {
 78  1
             if (!isSerializable(o)) {
 79  0
                 LOG.error("Attribute of class " + o.getClass().getName() + " with name " + se.getName() + " from source " + se.getSource().getClass().getName() + " was " + action + " to session and does not implement " + Serializable.class.getName());
 80  1
             } else if (!canBeSerialized((Serializable) o)){
 81  0
                 LOG.error("Attribute of class " + o.getClass().getName() + " with name " + se.getName() + " from source " + se.getSource().getClass().getName() + " was " + action + " to session and cannot be Serialized");
 82  
             }
 83  
         }
 84  1
     }
 85  
 
 86  
     /**
 87  
      * Simply tests whether the object implements the Serializable interface
 88  
      */
 89  
     private static boolean isSerializable(Object o) {
 90  1
         return o instanceof Serializable;
 91  
     }
 92  
 
 93  
     /**
 94  
      * Performs an expensive test of serializability by attempting to serialize the object graph
 95  
      */
 96  
     private static boolean canBeSerialized(Serializable o) {
 97  1
         ByteArrayOutputStream baos = null;
 98  1
         ObjectOutputStream out = null;
 99  
         try {
 100  1
             baos = new ByteArrayOutputStream(512);
 101  1
             out = new ObjectOutputStream(baos);
 102  1
             out.writeObject((Serializable) o);
 103  1
             return true;
 104  0
         } catch (IOException e) {
 105  0
             LOG.warn("error serializing object" , e);
 106  
         } finally {
 107  0
             try {
 108  1
                 if (baos != null) {
 109  
                     try {
 110  1
                         baos.close();
 111  0
                     } catch (IOException e) {
 112  0
                         LOG.warn("error closing stream" , e);
 113  1
                     }
 114  
                 }
 115  
             } finally {
 116  1
                 if (out != null) {
 117  
                     try {
 118  1
                         out.close();
 119  0
                     } catch (IOException e) {
 120  0
                          LOG.warn("error closing stream" , e);
 121  1
                     }
 122  
                 }
 123  
             }
 124  0
         }
 125  
 
 126  0
         return false;
 127  
     }
 128  
 }