Package net.sf.jasperreports.engine.fill

Source Code of net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer

/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2005 - 2009 Works, Inc. All rights reserved.
* http://www.works.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
*/

/*
* Licensed to Jaspersoft Corporation under a Contributer Agreement
*/
package net.sf.jasperreports.engine.fill;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.sf.jasperreports.engine.JRConstants;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRVirtualizable;
import net.sf.jasperreports.engine.JRVirtualizer;

import org.apache.commons.collections.LRUMap;
import org.apache.commons.collections.ReferenceMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
* Abstract base for LRU and serialization based virtualizer
*
* @author John Bindel
* @version $Id: JRAbstractLRUVirtualizer.java 3717 2010-04-09 10:01:33Z teodord $
*/
public abstract class JRAbstractLRUVirtualizer implements JRVirtualizer
{
  private static final Log log = LogFactory.getLog(JRAbstractLRUVirtualizer.class);

  protected static class CacheReference extends WeakReference
  {
    private final String id;

    public CacheReference(JRVirtualizable o, ReferenceQueue queue)
    {
      super(o, queue);
      id = o.getUID();
    }

    public String getId()
    {
      return id;
    }
  }

  /**
   * This class keeps track of how many objects are currently in memory, and
   * when there are too many, it pushes the last touched one to disk.
   */
  protected class Cache
  {
    protected class LRUScanMap extends LRUMap
    {
      private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;

      public LRUScanMap(int maxSize)
      {
        super(maxSize);
      }

      protected void removeLRU()
      {
        Map.Entry entry = getFirst();
        boolean found = isRemovable(entry);
        if (!found)
        {
          Iterator entriesIt = entrySet().iterator();
          entriesIt.next(); //skipping the first, which is already checked
          while(!found && entriesIt.hasNext())
          {
            entry = (Entry) entriesIt.next();
            found = isRemovable(entry);
          }
        }

        if (!found)
        {
          throw new JRRuntimeException("The virtualizer is used by more contexts than its in-memory cache size " + getMaximumSize());
        }

        Object key = entry.getKey();
        Object value = entry.getValue();
        this.remove(key);
        processRemovedLRU(key,value);
      }

      protected boolean isRemovable(Map.Entry entry)
      {
        JRVirtualizable value = getMapValue(entry.getValue());
        return value == null || !lastObjectSet.containsKey(value);
      }

      protected void processRemovedLRU(Object key, Object value)
      {
        JRVirtualizable o = getMapValue(value);
        if (o != null)
        {
          virtualizeData(o);
        }
      }
    }

    private final ReferenceQueue refQueue;
    private final LRUScanMap map;

    Cache(int maxSize)
    {
      map = new LRUScanMap(maxSize);
      refQueue = new ReferenceQueue();
    }

    protected JRVirtualizable getMapValue(Object val)
    {
      JRVirtualizable o;
      if (val == null)
      {
        o = null;
      }
      else
      {
        Reference ref = (Reference) val;
        if (ref.isEnqueued())
        {
          o = null;
        }
        else
        {
          o = (JRVirtualizable) ref.get();
        }
      }
      return o;
    }

    protected Object toMapValue(JRVirtualizable val)
    {
      return val == null ? null : new CacheReference(val, refQueue);
    }

    protected void purge()
    {
      CacheReference ref;
      while ((ref = (CacheReference) refQueue.poll()) != null)
      {
        map.remove(ref.getId());
      }
    }

    public JRVirtualizable get(String id)
    {
      purge();

      return getMapValue(map.get(id));
    }

    public JRVirtualizable put(String id, JRVirtualizable o)
    {
      purge();

      return getMapValue(map.put(id, toMapValue(o)));
    }

    public JRVirtualizable remove(String id)
    {
      purge();

      return getMapValue(map.remove(id));
    }

    public Iterator idIterator()
    {
      purge();

      final Iterator valsIt = map.values().iterator();
      return new Iterator()
      {
        public boolean hasNext()
        {
          return valsIt.hasNext();
        }

        public Object next()
        {
          CacheReference ref = (CacheReference) valsIt.next();
          return ref.getId();
        }

        public void remove()
        {
          valsIt.remove();
        }
      };
    }
  }

  protected static final int CLASSLOADER_IDX_NOT_SET = -1;

  protected static boolean isAncestorClassLoader(ClassLoader loader)
  {
    for (
        ClassLoader ancestor = JRAbstractLRUVirtualizer.class.getClassLoader();
        ancestor != null;
        ancestor = ancestor.getParent())
    {
      if (ancestor.equals(loader))
      {
        return true;
      }
    }
    return false;
  }

  protected final Map classLoadersIndexes = new HashMap();
  protected final List classLoadersList = new ArrayList();

  protected class ClassLoaderAnnotationObjectOutputStream extends ObjectOutputStream
  {
    public ClassLoaderAnnotationObjectOutputStream(OutputStream out) throws IOException
    {
      super(out);
    }

    protected void annotateClass(Class clazz) throws IOException
    {
      super.annotateClass(clazz);

      ClassLoader classLoader = clazz.getClassLoader();
      int loaderIdx;
      if (clazz.isPrimitive()
          || classLoader == null
          || isAncestorClassLoader(classLoader))
      {
        loaderIdx = CLASSLOADER_IDX_NOT_SET;
      }
      else
      {
        Integer idx = (Integer) classLoadersIndexes.get(classLoader);
        if (idx == null)
        {
          idx = Integer.valueOf(classLoadersList.size());
          classLoadersIndexes.put(classLoader, idx);
          classLoadersList.add(classLoader);
        }
        loaderIdx = idx.intValue();
      }

      writeShort(loaderIdx);
    }
  }

  protected class ClassLoaderAnnotationObjectInputStream extends ObjectInputStream
  {
    public ClassLoaderAnnotationObjectInputStream(InputStream in) throws IOException
    {
      super(in);
    }

    protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException
    {
      Class clazz;
      try
      {
        clazz = super.resolveClass(desc);
        readShort();
      }
      catch (ClassNotFoundException e)
      {
        int loaderIdx = readShort();
        if (loaderIdx == CLASSLOADER_IDX_NOT_SET)
        {
          throw e;
        }

        ClassLoader loader = (ClassLoader) classLoadersList.get(loaderIdx);
        clazz = Class.forName(desc.getName(), false, loader);
      }

      return clazz;
    }


  }

  private final Cache pagedIn;

  private final ReferenceMap pagedOut;

  protected JRVirtualizable lastObject;
  protected ReferenceMap lastObjectMap;
  protected ReferenceMap lastObjectSet;

  private boolean readOnly;

  /**
   * @param maxSize
   *            the maximum size (in JRVirtualizable objects) of the paged in
   *            cache.
   */
  protected JRAbstractLRUVirtualizer(int maxSize)
  {
    this.pagedIn = new Cache(maxSize);
    this.pagedOut = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
    this.lastObject = null;

    this.lastObjectMap = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
    this.lastObjectSet = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.HARD);
  }

  protected synchronized final boolean isPagedOut(String id)
  {
    return pagedOut.containsKey(id);
  }

  protected synchronized boolean isPagedOutAndTouch(JRVirtualizable o, String uid)
  {
    boolean virtualized = isPagedOut(uid);
    if (!virtualized)
    {
      touch(o);
    }
    return virtualized;
  }

  protected final void setLastObject(JRVirtualizable o)
  {
    if (lastObject != o)
    {
      if (o != null)
      {
        JRVirtualizationContext context = o.getContext();
        Object ownerLast = lastObjectMap.get(context);
        if (ownerLast != o)
        {
          if (ownerLast != null)
          {
            lastObjectSet.remove(ownerLast);
          }
          lastObjectMap.put(context, o);
          lastObjectSet.put(o, Boolean.TRUE);
        }
      }
      this.lastObject = o;
    }
  }

  /**
   * Sets the read only mode for the virtualizer.
   * <p/>
   * When in read-only mode, the virtualizer assumes that virtualizable objects are final
   * and any change in a virtualizable object's data is discarded.
   * <p/>
   * When the virtualizer is used for multiple virtualization contexts (in shared mode),
   * calling this method would override the read-only flags from all the contexts and all the
   * objects will be manipulated in read-only mode.
   * Use {@link JRVirtualizationContext#setReadOnly(boolean) JRVirtualizationContext.setReadOnly(boolean)}
   * to set the read-only mode for one specific context.
   *
   * @param ro the read-only mode to set
   */
  public void setReadOnly(boolean ro)
  {
    this.readOnly = ro;
  }


  /**
   * Determines whether the virtualizer is in read-only mode.
   *
   * @return whether the virtualizer is in read-only mode
   * @see #setReadOnly(boolean)
   */
  public boolean isReadOnly()
  {
    return readOnly;
  }

  protected final boolean isReadOnly(JRVirtualizable o)
  {
    return readOnly || o.getContext().isReadOnly();
  }

  public synchronized void registerObject(JRVirtualizable o)
  {
    setLastObject(o);
    JRVirtualizable old = pagedIn.put(o.getUID(), o);
    if (old != null)
    {
      pagedIn.put(o.getUID(), old);
      throw new IllegalStateException("Wrong object stored with UID \"" + o.getUID() + "\"");
    }
  }

  public void deregisterObject(JRVirtualizable o)
  {
    String uid = o.getUID();

    //try to remove virtual data
    try
    {
      dispose(o.getUID());
    }
    catch (Exception e)
    {
      log.error("Error removing virtual data", e);
      //ignore
    }

    synchronized(this)
    {
      JRVirtualizable oldIn = pagedIn.remove(uid);
      if (oldIn != null)
      {
        if (oldIn != o)
        {
          pagedIn.put(uid, oldIn);
          throw new IllegalStateException("Wrong object stored with UID \"" + o.getUID() + "\"");
        }
      }
      else
      {
        Object oldOut = pagedOut.remove(uid);
        if (oldOut != null && oldOut != o)
        {
          pagedOut.put(uid, oldOut);
          throw new IllegalStateException("Wrong object stored with UID \"" + o.getUID() + "\"");
        }
      }

      // We don't really care if someone deregisters an object
      // that's not registered.
    }
  }

  public synchronized void touch(JRVirtualizable o)
  {
    // If we just touched this object, don't touch it again.
    if (this.lastObject != o)
    {
      setLastObject(pagedIn.get(o.getUID()));
    }
  }

  public void requestData(JRVirtualizable o)
  {
    String uid = o.getUID();
    if (isPagedOutAndTouch(o, uid))
    {
      // unvirtualize
      try
      {
        pageIn(o);
      }
      catch (IOException e)
      {
        log.error("Error devirtualizing object", e);
        throw new JRRuntimeException(e);
      }

      o.afterInternalization();

      synchronized (this)
      {
        setLastObject(o);
        pagedOut.remove(uid);
        pagedIn.put(uid, o);
      }
    }
  }

  public void clearData(JRVirtualizable o)
  {
    String uid = o.getUID();
    if (isPagedOutAndTouch(o, uid))
    {
      // remove virtual data
      dispose(uid);

      synchronized (this)
      {
        pagedOut.remove(uid);
      }
    }
  }

  public void virtualizeData(JRVirtualizable o)
  {
    String uid = o.getUID();
    if (!isPagedOut(uid))
    {
      o.beforeExternalization();

      // virtualize
      try
      {
        pageOut(o);
      }
      catch (IOException e)
      {
        log.error("Error virtualizing object", e);
        throw new JRRuntimeException(e);
      }

      o.afterExternalization();

      // Wait until we know it worked before tossing the data.
      o.removeVirtualData();

      synchronized (this)
      {
        pagedOut.put(uid, o);
      }
    }
  }

  protected void finalize() throws Throwable //NOSONAR
  {
    cleanup();

    super.finalize();
  }

  /**
   * Writes serialized indentity and virtual data of a virtualizable object to a stream.
   *
   * @param o the serialized object
   * @param out the output stream
   * @throws JRRuntimeException
   */
  protected final void writeData(JRVirtualizable o, OutputStream out)
      throws JRRuntimeException
  {
    try
    {
      ObjectOutputStream oos = new ClassLoaderAnnotationObjectOutputStream(out);
      oos.writeObject(o.getIdentityData());
      oos.writeObject(o.getVirtualData());
      oos.flush();
    }
    catch (IOException e)
    {
      log.error("Error virtualizing object", e);
      throw new JRRuntimeException(e);
    }
  }


  /**
   * Reads serialized identity and virtual data for a virtualizable object
   * from a stream.
   *
   * @param o the virtualizable object
   * @param in the input stream
   * @throws JRRuntimeException
   */
  protected final void readData(JRVirtualizable o, InputStream in)
      throws JRRuntimeException
  {
    try
    {
      ObjectInputStream ois = new ClassLoaderAnnotationObjectInputStream(in);
      o.setIdentityData(ois.readObject());
      o.setVirtualData(ois.readObject());
    }
    catch (IOException e)
    {
      log.error("Error devirtualizing object", e);
      throw new JRRuntimeException(e);
    }
    catch (ClassNotFoundException e)
    {
      log.error("Error devirtualizing object", e);
      throw new JRRuntimeException(e);
    }
  }

  protected synchronized void reset()
  {
    readOnly = false;
  }

  protected final void disposeAll()
  {
    // Remove all paged-out swap files.
    for (Iterator it = pagedOut.keySet().iterator(); it.hasNext();)
    {
      String id = (String) it.next();
      try
      {
        dispose(id);
        it.remove();
      }
      catch (Exception e)
      {
        log.error("Error cleaning up virtualizer.", e);
        // Do nothing because we want to try to remove all swap files.
      }
    }

    for (Iterator it = pagedIn.idIterator(); it.hasNext();)
    {
      String id = (String) it.next();
      try
      {
        dispose(id);
        it.remove();
      }
      catch (Exception e)
      {
        log.error("Error cleaning up virtualizer.", e);
        // Do nothing because we want to try to remove all swap files.
      }
    }
  }

  /**
   * Writes a virtualizable object's data to an external storage.
   *
   * @param o a virtualizable object
   * @throws IOException
   */
  protected abstract void pageOut(JRVirtualizable o) throws IOException;


  /**
   * Reads a virtualizable object's data from an external storage.
   *
   * @param o a virtualizable object
   * @throws IOException
   */
  protected abstract void pageIn(JRVirtualizable o) throws IOException;


  /**
   * Removes the external data associated with a virtualizable object.
   *
   * @param virtualId the ID of the virtualizable object
   */
  protected abstract void dispose(String virtualId);
}
TOP

Related Classes of net.sf.jasperreports.engine.fill.JRAbstractLRUVirtualizer

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.