Package er.extensions.components

Source Code of er.extensions.components.ERXCachingWrapper$Entry

package er.extensions.components;

import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.webobjects.appserver.WOActionResults;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WORequest;
import com.webobjects.appserver.WOResponse;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSKeyValueCodingAdditions;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSPropertyListSerialization;

import er.extensions.foundation.ERXAssert;

/**
* Wrapper that caches its content based on a set of bindings. Use this component to wrap
* parts of your HTML whose generation is costly. <br />
* Valid keys would be for example:
* <ul>
*   <li><code>parent.isEnabled</code>, where isEnabled would be some method on the parent.
*   <li><code>session.user.name</code>
*   <li><code>headers.hostName</code>
*   <li><code>formValues.oid</code>
*   <li><code>session.localizer.language</code>
* </ul>
* Basically, you would put there any key whose value would cause the content to change.
* Session IDs are replaced automatically. Don't use this wrapper if the content contains
* component actions. Drop only stateless components in this wrapper.
*
* @binding keys the keys to use for caching
* @binding duration the duration the entry stays in the cache
* @binding entryName the name to cache on
*
* @author ak on 20.01.05
*/
//ENHANCEME cache should get reaped every so often and remove stale entries.
public class ERXCachingWrapper extends ERXStatelessComponent {
  /**
   * Do I need to update serialVersionUID?
   * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
   * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
   */
  private static final long serialVersionUID = 1L;


    /** logging support */
    private static final Logger log = Logger.getLogger(ERXCachingWrapper.class);

    /** The cached entries */
    protected static Map cache = Collections.synchronizedMap(new HashMap() {
      /**
       * Do I need to update serialVersionUID?
       * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
       * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
       */
      private static final long serialVersionUID = 1L;

      @Override
      public Object get(Object key) {
        Entry result = (Entry) super.get(key);
        if(result != null) {
          if(!result.isActive()) {
            remove(key);
            result = null;
          }
        }
        return result;
      }
    });
   
    /** Simply cache entry class. It caches a string for a duration and can replace the session ID on retrieval. */
    protected static class Entry implements Serializable{
      /**
       * Do I need to update serialVersionUID?
       * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
       * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
       */
      private static final long serialVersionUID = 1L;

      private long insertTime;
      private long duration;
      private String content;
      private String sessionID;
     
      public Entry(String aContent, long aDuration, String aSessionID) {
        insertTime = System.currentTimeMillis();
        content = aContent;
        duration = aDuration;
        sessionID = aSessionID;
      }

    public boolean isActive() {
      return System.currentTimeMillis() - (insertTime + duration) < 0;
    }
   
    public String content(WOContext arg1) {
      if(sessionID != null) {
        return content.replaceAll(sessionID, arg1.session().sessionID());
      }
      return content;
    }
    }

    protected NSArray keys;
    protected String entryName;
    protected Long cacheDuration;
    protected Entry entry;
    protected NSDictionary values;
   
    /**
     * Public constructor
     * @param context the context
     */
    public ERXCachingWrapper(WOContext context) {
        super(context);
    }
   
    @Override
    public void awake() {
      super.awake();
      keys = null;
      entryName = null;
      cacheDuration = null;
      values = null;
       entry = (Entry) cache.get(values());
    }
   
    protected NSArray keys() {
      if(keys == null) {
        Object value = valueForBinding("keys");
        if(value instanceof NSArray) {
          keys = (NSArray)value;
        } else if(value instanceof String) {
          keys = (NSArray) NSPropertyListSerialization.propertyListFromString((String) value);
        } else if (value != null) {
          throw new IllegalArgumentException("keys must be a NSArray or a property list String");
        }
        if(keys == null) {
          keys = NSArray.EmptyArray;
        }
      }
      return keys;
    }
   
    /**
     * Returns the request headers as a KVC object.
     */
    // ENHANCEME use symbolic names like "remoteHost" to broker between all those different adaptor keys
    public NSKeyValueCoding headers() {
      return new NSKeyValueCoding() {
            public Object valueForKey(String s) {
              return context().request().headerForKey(s);
            }

            public void takeValueForKey(Object obj, String s) {
            }
       
      };
    }
   
    /**
     * Returns the form values as a KVC object.
     */
    public NSKeyValueCoding formValues() {
      return new NSKeyValueCoding() {
            public Object valueForKey(String s) {
              return context().request().formValueForKey(s);
            }

            public void takeValueForKey(Object obj, String s) {
            }
       
      };
    }
   
    protected String entryName() {
      if(entryName == null) {
        entryName = (String)valueForBinding("entryName");
        }
        ERXAssert.PRE.notNull("cacheEntryName is required", entryName);
        return entryName;
    }
   
    protected long cacheDuration() {
        if(cacheDuration == null) {
            Number value = (Number)valueForBinding("duration");
            if(value == null) {
                cacheDuration = Long.valueOf(60L*1000L);
            } else {
                cacheDuration = Long.valueOf(value.longValue());
            }
        }
        return cacheDuration.longValue();
    }
   
    protected NSDictionary values() {
      if(values == null) {
        NSMutableDictionary result = new NSMutableDictionary();
        for(Enumeration e = keys().objectEnumerator(); e.hasMoreElements();) {
          String keyPath = (String)e.nextElement();
          Object value = NSKeyValueCodingAdditions.Utility.valueForKeyPath(this, keyPath);
          if(value != null) {
            result.setObjectForKey(value, keyPath);
          }
        }
        result.setObjectForKey(entryName(), "ERXCachingWrapper.entryName");
        values = result.immutableClone();
      }
      return values;
    }
   
    @Override
  public void takeValuesFromRequest(WORequest request, WOContext context) {
    if(entry == null) {
      super.takeValuesFromRequest(request, context);
    }
  }
 
    @Override
  public WOActionResults invokeAction(WORequest request, WOContext context) {
    if(entry == null) {
      return super.invokeAction(request, context);
    }
    return null;
  }
 
    @Override
  public void appendToResponse(WOResponse response, WOContext context) {
    if(entry == null) {
      WOResponse newResponse = application().createResponseInContext(context);
      newResponse.setHeaders(response.headers());
      newResponse.setUserInfo(response.userInfo());
      super.appendToResponse(newResponse, context);
      String content = newResponse.contentString();
      entry = new Entry(content, cacheDuration(), (context.hasSession() ? context.session().sessionID() : null));
      cache.put(values(), entry);
    }
    String content = entry.content(context);
    response.appendContentString(content);
  }
}
TOP

Related Classes of er.extensions.components.ERXCachingWrapper$Entry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.