Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2005 - 2014 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 .
*/
/*
* 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.OutputStream;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRVirtualizable;
import net.sf.jasperreports.engine.JRVirtualizer;
import net.sf.jasperreports.engine.util.VirtualizationSerializer;
import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstract base for LRU and serialization based virtualizer
*
* @author John Bindel
*/
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
{
private final int maxSize;
private final ReferenceQueue refQueue;
private final LinkedHashMap map;
Cache(int maxSize)
{
this.maxSize = maxSize;
map = new LinkedHashMap(16, 0.75f, true);
refQueue = new ReferenceQueue();
}
protected JRVirtualizable getMapValue(CacheReference val)
{
JRVirtualizable o;
if (val == null)
{
o = null;
}
else
{
if (val.isEnqueued())
{
o = null;
}
else
{
o = val.get();
}
}
return o;
}
protected CacheReference 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 boolean contains(String id)
{
purge();
return map.containsKey(id);
}
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 List evictionCandidates()
{
if (map.size() <= maxSize)
{
return Collections.emptyList();
}
int candidateCount = map.size() - maxSize;
List candidates = new ArrayList();
Iterator> mapIterator = map.entrySet().iterator();
while (candidates.size() < candidateCount && mapIterator.hasNext())
{
Entry entry = mapIterator.next();
JRVirtualizable value = getMapValue(entry.getValue());
if (value == null)
{
// this entry will get removed by purge()
--candidateCount;
}
else if (isEvictable(value))
{
if (log.isDebugEnabled())
{
log.debug("LRU eviction candidate: " + entry.getKey());
}
candidates.add(value);
}
}
if (candidates.size() < candidateCount)
{
log.debug("The virtualizer is used by more contexts than its in-memory cache size " + maxSize);
}
return candidates;
}
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 String next()
{
CacheReference ref = valsIt.next();
return ref.getId();
}
public void remove()
{
valsIt.remove();
}
};
}
}
protected final VirtualizationSerializer serializer = new VirtualizationSerializer();
private final Cache pagedIn;
private final ReferenceMap pagedOut;
protected volatile WeakReference lastObjectRef;
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.lastObjectRef = 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 JRVirtualizable lastObject()
{
WeakReference ref = lastObjectRef;
JRVirtualizable object = ref == null ? null : ref.get();
return object;
}
protected final void setLastObject(JRVirtualizable o)
{
JRVirtualizable currentLast = lastObject();
if (o != null && currentLast != o)
{
// lastObject is mostly an optimization, we don't care if we don't have atomic operations here
this.lastObjectRef = new WeakReference(o);
synchronized (this)
{
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);
}
}
}
}
/**
* Sets the read only mode for the virtualizer.
*
* When in read-only mode, the virtualizer assumes that virtualizable objects are final
* and any change in a virtualizable object's data is discarded.
*
* 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 void registerObject(JRVirtualizable o)
{
if (log.isDebugEnabled())
{
log.debug("registering " + o.getUID());
}
synchronized (this)
{
setLastObject(o);
JRVirtualizable old = pagedIn.put(o.getUID(), o);
if (old != null && old != o)
{
pagedIn.put(o.getUID(), old);
throw new IllegalStateException("Wrong object stored with UID \"" + o.getUID() + "\"");
}
}
if (log.isDebugEnabled())
{
log.debug("registered object " + o + " with id " + o.getUID());
}
evict();
}
protected boolean isEvictable(JRVirtualizable value)
{
return value.getContext().isDisposed() || !lastObjectSet.containsKey(value);
}
protected void evict()
{
//FIXME lucianc also attempt to evict on non-put operations if the last evict was not successful
//FIXME lucianc prevent two threads from attempting to evict the same objects
List candidates;
synchronized (this)
{
candidates = pagedIn.evictionCandidates();
}
for (JRVirtualizable o : candidates)
{
String uid = o.getUID();
if (o.getContext().tryLock())
{
try
{
boolean evictable;
synchronized (this)
{
// check again due to sequential locking
evictable = pagedIn.contains(uid) && isEvictable(o);
if (evictable)
{
pagedIn.remove(uid);
}
}
if (evictable)
{
if (log.isDebugEnabled())
{
log.debug("evicting " + uid);
}
if (!o.getContext().isDisposed())
{
virtualizeData(o);
}
}
else
{
if (log.isDebugEnabled())
{
log.debug("no longer evictable: " + uid);
}
}
}
finally
{
o.getContext().unlock();
}
}
else
{
if (log.isDebugEnabled())
{
log.debug("couldn't lock for eviction " + uid);
}
}
}
}
public void deregisterObject(JRVirtualizable o)
{
String uid = o.getUID();
if (log.isDebugEnabled())
{
log.debug("deregistering " + uid);
}
//try to remove virtual data
try
{
dispose(o);
}
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() + "\"");
}
Object contextLast = lastObjectMap.get(o.getContext());
if (contextLast == o)
{
lastObjectMap.remove(o.getContext());
lastObjectSet.remove(o);
}
}
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.
}
if (log.isDebugEnabled())
{
log.debug("deregistered object " + o + " with id " + o.getUID());
}
}
public void touch(JRVirtualizable o)
{
// If we just touched this object, don't touch it again.
if (lastObject() != o)
{
//FIXME lucianc this doesn't scale well with concurrency
// get the object from the map to update LRU order
JRVirtualizable internalObject;
synchronized (this)
{
internalObject = pagedIn.get(o.getUID());
}
setLastObject(internalObject);
}
}
public void requestData(JRVirtualizable o)
{
String uid = o.getUID();
boolean evictRequired = false;
o.getContext().lock();
try
{
if (isPagedOutAndTouch(o, uid))
{
if (log.isDebugEnabled())
{
log.debug("internalizing " + uid);
}
// unvirtualize
try
{
pageIn(o);
}
catch (IOException e)
{
log.error("Error devirtualizing object", e);
throw new JRRuntimeException(e);
}
synchronized (this)
{
setLastObject(o);
pagedOut.remove(uid);
pagedIn.put(uid, o);
}
o.afterInternalization();
evictRequired = true;
}
}
finally
{
o.getContext().unlock();
}
if (evictRequired)
{
evict();
}
}
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))
{
if (log.isDebugEnabled())
{
log.debug("externalizing " + 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
{
serializer.writeData(o, out);
}
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
{
serializer.readData(o, in);
}
catch (IOException 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 = 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 = 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;
protected void dispose(JRVirtualizable o)
{
dispose(o.getUID());
}
/**
* Removes the external data associated with a virtualizable object.
*
* @param virtualId the ID of the virtualizable object
*/
protected abstract void dispose(String virtualId);
}