001 package jigcell.compare.data;
002
003 import java.beans.DefaultPersistenceDelegate;
004 import java.beans.Encoder;
005 import java.beans.Expression;
006 import java.beans.XMLDecoder;
007 import java.beans.XMLEncoder;
008 import java.io.BufferedInputStream;
009 import java.io.BufferedOutputStream;
010 import java.io.File;
011 import java.io.FileInputStream;
012 import java.io.FileOutputStream;
013 import java.io.IOException;
014 import java.io.ObjectInputStream;
015 import java.io.ObjectOutputStream;
016 import java.lang.ref.SoftReference;
017 import java.util.zip.GZIPInputStream;
018 import java.util.zip.GZIPOutputStream;
019 import jigcell.compare.IDataElement;
020 import jigcell.compare.impl.Compare;
021 import jigcell.compare.impl.ExceptionRecorder;
022
023 /**
024 * A data element that provides a memory sensitive cache wrapped around another data element.
025 *
026 * <p>
027 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details.
028 * </p>
029 *
030 * @author Nicholas Allen
031 */
032
033 public class CachedDataElement extends DataElement {
034
035 /**
036 * Prefix for temporary cache files
037 */
038
039 protected final static String TEMPFILE_PREFIX = "jigcell";
040
041 /**
042 * Cached exception recorder for this transferer
043 */
044
045 protected transient ExceptionRecorder exception;
046
047 /**
048 * File where the data element can be found
049 */
050
051 protected transient File file;
052
053 /**
054 * Element for use in memory locking
055 */
056
057 protected transient IDataElement cache;
058
059 /**
060 * Data element to cache
061 */
062
063 protected transient SoftReference ref;
064
065 /**
066 * Persists the element by creating a new wrapper around the cached element.
067 */
068
069 protected static class CachedDataElementDelegate extends DefaultPersistenceDelegate {
070 protected Expression instantiate (Object o, Encoder encoder) {
071 return new Expression (o, o.getClass (), "new", new Object [] {((CachedDataElement) o).getCachedElement ()});
072 }
073 }
074
075 static {
076 Compare.addDelegate (CachedDataElement.class, new CachedDataElementDelegate ());
077 }
078
079 /**
080 * Creates a new data element cache.
081 *
082 * @param element Element to cache
083 */
084
085 public CachedDataElement (IDataElement element) throws IOException {
086 if (element == null)
087 throw new IllegalArgumentException ();
088 exception = new ExceptionRecorder ("Error encountered by element store.", true, false, true);
089 createCachedElement (element);
090 }
091
092 /**
093 * Creates a new data element cache.
094 */
095
096 private CachedDataElement () {
097 throw new UnsupportedOperationException ();
098 }
099
100 /**
101 * {@inheritDoc}
102 */
103
104 public boolean getBooleanValue (long pos) {
105 return getCachedElement ().getBooleanValue (pos);
106 }
107
108 /**
109 * {@inheritDoc}
110 */
111
112 public long getIntegralValue (long pos) {
113 return getCachedElement ().getIntegralValue (pos);
114 }
115
116 /**
117 * {@inheritDoc}
118 */
119
120 public long getLength () {
121 return getCachedElement ().getLength ();
122 }
123
124 /**
125 * {@inheritDoc}
126 */
127
128 public IDataElement getListValue (long pos) {
129 return getCachedElement ().getListValue (pos);
130 }
131
132 /**
133 * {@inheritDoc}
134 */
135
136 public String getLiteralValue (long pos) {
137 return getCachedElement ().getLiteralValue (pos);
138 }
139
140 /**
141 * {@inheritDoc}
142 */
143
144 public double getRealValue (long pos) {
145 return getCachedElement ().getRealValue (pos);
146 }
147
148 /**
149 * {@inheritDoc}
150 */
151
152 public IDataElement.Type getType () {
153 return getCachedElement ().getType ();
154 }
155
156 /**
157 * {@inheritDoc}
158 */
159
160 public IDataElement.Type getType (long pos) {
161 return getCachedElement ().getType (pos);
162 }
163
164 /**
165 * {@inheritDoc}
166 */
167
168 public synchronized boolean isAvailable () {
169 return lockCount > 0 || ref.get () != null && super.isAvailable ();
170 }
171
172 /**
173 * {@inheritDoc}
174 */
175
176 public synchronized void memoryLock () {
177 if (lockCount++ == 0) {
178 cache = getCachedElement ();
179 ref = new SoftReference (cache);
180 }
181 }
182
183 /**
184 * {@inheritDoc}
185 */
186
187 public synchronized void memoryUnlock () {
188 if (--lockCount == 0)
189 cache = null;
190 }
191
192 /**
193 * Creates the backend representation of the cached element.
194 */
195
196 protected void createCachedElement (IDataElement element) throws IOException {
197 ref = new SoftReference (element);
198 file = File.createTempFile (TEMPFILE_PREFIX, null, null);
199 XMLEncoder encoder = new XMLEncoder (new GZIPOutputStream (new BufferedOutputStream (new FileOutputStream (file))));
200 encoder.setExceptionListener (exception);
201 Compare.updateEncoder (encoder);
202 encoder.writeObject (element);
203 encoder.close ();
204 file.deleteOnExit ();
205 }
206
207 /**
208 * Retrieves the cached element for use.
209 */
210
211 protected synchronized IDataElement getCachedElement () {
212 if (cache != null)
213 return cache;
214 IDataElement element = (IDataElement) ref.get ();
215 if (element == null)
216 try {
217 XMLDecoder decoder = new XMLDecoder (new GZIPInputStream (new BufferedInputStream (new FileInputStream (file))), this, exception);
218 element = (IDataElement) decoder.readObject ();
219 decoder.close ();
220 ref = new SoftReference (element);
221 } catch (IOException e) {
222 Compare.assertion ("Unable to read cache file.", e);
223 }
224 return element;
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230
231 protected ExceptionRecorder getExceptionRecorder () {
232 return exception;
233 }
234
235 private void readObject (ObjectInputStream objectIn) throws IOException, ClassNotFoundException {
236 createCachedElement ((IDataElement) objectIn.readObject ());
237 objectIn.defaultReadObject ();
238 }
239
240 private void writeObject (ObjectOutputStream objectOut) throws IOException {
241 objectOut.writeObject (getCachedElement ());
242 objectOut.defaultWriteObject ();
243 }
244 }