001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.objectmanager.cache;
031
032import java.io.BufferedInputStream;
033import java.io.File;
034import java.io.FileInputStream;
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.ObjectInputStream;
038import java.io.Serializable;
039import java.util.Date;
040import java.util.Iterator;
041import java.util.List;
042import java.util.Vector;
043
044import org.apache.commons.logging.Log;
045import org.apache.commons.logging.LogFactory;
046
047/**
048 * Represents an item in the cache
049 */
050public abstract class DataCacheObject extends CacheObject implements Runnable,
051                Serializable {
052        /**
053         * Description of the Field
054         */
055        protected final static int CACHE_BUSY = 1;
056        /**
057         * Description of the Field
058         */
059        protected final static int CACHE_ERROR = 2;
060        /**
061         * Description of the Field
062         */
063        protected final static int CACHE_COMPLETE = 3;
064        /**
065         * Description of the Field
066         */
067        protected final static int CACHE_EMPTY = 4;
068
069        /**
070         * Description of the Field
071         */
072        private transient Thread mThread = null;
073
074        /**
075         * Description of the Field
076         */
077        private static Log log;
078        private static boolean isDebugging;
079
080        static {
081                log = LogFactory
082                                .getLog("org.kepler.objectmanager.cache.DataCacheObject");
083                isDebugging = log.isDebugEnabled();
084        }
085
086        private final static String cachedatapath = CacheManager.cachePath
087                        + File.separator + "cachedata" + File.separator;
088
089        /**
090         * Description of the Field
091         */
092        private Date mCreatedDate = new Date();
093        /**
094         * Description of the Field
095         */
096        private String mResourceName = null;
097        /**
098         * Absolute filename of data file.
099         */
100        private String mLocalFileName = null;
101
102        /**
103         * Description of the Field
104         */
105        private transient Vector mListeners = new Vector();
106
107        /**
108         * Description of the Field
109         */
110        private int mStatus = CACHE_EMPTY;
111
112        /**
113         * Constructor
114         */
115        public DataCacheObject() {
116        }
117
118        /**
119         *@param createdDate
120         *            The mCreatedDate to set.
121         */
122        public final void setCreatedDate(Date createdDate) {
123                mCreatedDate = createdDate;
124        }
125
126        /**
127         *@param localFileName
128         *            The mLocalFileName to set.
129         */
130        public final void setAbsoluteFileName(String localFileName) {
131                mLocalFileName = localFileName;
132        }
133
134        /**
135         *@param localFileName
136         *            The mLocalFileName to set.
137         */
138        public final void setBaseFileName(String localFileName) {
139                mLocalFileName = cachedatapath + localFileName;
140        }
141
142        /**
143         *@param resourceName
144         *            The mResourceName to set.
145         */
146        public final void setResourceName(String resourceName) {
147                mResourceName = resourceName;
148        }
149
150        /**
151         * 
152         */
153        public final Object getObject() {
154                return new File(mLocalFileName);
155        }
156
157        public final File getFile() {
158                return (File) getObject();
159        }
160
161        /**
162         * Return the data as an InputStream. The data is read from the cached file
163         * on disk, so this method assumes the cache item has already been
164         * retrieved. If not, or if there is an error, then the return value will be
165         * null.
166         * 
167         *@return InputStream representing the data
168         */
169        public final InputStream getDataInputStream() {
170                BufferedInputStream bis = null;
171
172                if (mLocalFileName != null && mLocalFileName.length() > 0) {
173                        try {
174                                File file = getFile();
175
176                                if (file != null && file.exists()) {
177                                        FileInputStream fis = new FileInputStream(file);
178
179                                        if (fis != null) {
180                                                bis = new BufferedInputStream(fis);
181                                        }
182                                }
183                        } catch (Exception e) {
184                                System.err.println(e);
185                        }
186                } else {
187                        log.debug("loadData - mLocalFileName was null: \n  " + super.getName()
188                                        + "  \n" + mResourceName + "  \n" + mLocalFileName);
189                }
190                return bis;
191        }
192
193        /**
194         * Returns whether it is in empty state
195         * 
196         *       */
197        public final boolean isEmpty() {
198                return mStatus == CACHE_EMPTY;
199        }
200
201        /**
202         * Return the status of getting the data
203         * 
204         * 
205         *       */
206        public final boolean isReady() {
207                return mStatus == CACHE_COMPLETE;
208        }
209
210        /**
211         * Returns whether it is busy getting the data
212         * 
213         *       */
214        public final boolean isBusy() {
215                return mStatus == CACHE_BUSY;
216        }
217
218        /**
219         * Returns whether it is in error state
220         * 
221         *       */
222        public final boolean isError() {
223                return mStatus == CACHE_ERROR;
224        }
225
226        /**
227         *@return Returns the mCreatedDate.
228         */
229        public final Date getCreatedDate() {
230                return mCreatedDate;
231        }
232
233        /**
234         *@return Returns the mLocalFileName.
235         */
236        public final String getAbsoluteFileName() {
237                return mLocalFileName;
238        }
239
240        /**
241         *@return Returns the mLocalFileName.
242         */
243        public final String getBaseFileName() {
244                int inx = mLocalFileName.lastIndexOf(File.separator);
245
246                if (inx > -1) {
247                        return mLocalFileName.substring(inx + 1);
248                }
249                return mLocalFileName;
250        }
251
252        /**
253         *@return Returns the mResourceName.
254         */
255        public final String getResourceName() {
256                return mResourceName;
257        }
258
259        /**
260         *@return Returns the mStatus.
261         */
262        protected final int getStatus() {
263                return mStatus;
264        }
265
266        /**
267         * Add a listenero
268         * 
269         *@param aListener
270         *            the listener to add
271         */
272        public final void addListener(DataCacheListener aListener) {
273                // If the listener is null, don't do anything.
274                if (aListener == null) {
275                        return;
276                }
277                // If the object is already finished, then just notify.
278                if (isReady() || isError()) {
279                        notifyOne(aListener);
280                        return;
281                }
282                // Otherwise, we need to add the listener to the list
283                synchronized (mListeners) {
284                        mListeners.addElement(aListener);
285                }
286        }
287
288        /**
289         * Removes a listener
290         * 
291         *@param aListener
292         *            the listener to remove
293         */
294        public final void removeListener(DataCacheListener aListener) {
295                if (aListener == null) {
296                        return;
297                }
298                synchronized (mListeners) {
299                        mListeners.removeElement(aListener);
300                }
301        }
302
303        /**
304         * Removes all the listeners
305         */
306        public final void removeAllListeners() {
307                synchronized (mListeners) {
308                        mListeners.clear();
309                }
310        }
311
312        /**
313         * Notifies the listener that the "getting" of the data has completed
314         */
315        public final void notifyListeners() {
316                List localcopy;
317                synchronized (mListeners) {
318                        localcopy = (List) mListeners.clone();
319                }
320                for (Iterator i = localcopy.iterator(); i.hasNext();) {
321                        DataCacheListener l = (DataCacheListener) i.next();
322                        notifyOne(l);
323                }
324        }
325
326        private final void notifyOne(DataCacheListener l) {
327                l.complete(this);
328        }
329
330        /**
331         * Refreshes the data from the original source
332         * 
333         *@param aListener
334         */
335        public final void refresh(DataCacheListener aListener) {
336                clear();
337                // System.err.println("Refresh Name "+mName +
338                // "has new file name"+mLocalFileName);
339                addListener(aListener);
340                mCreatedDate = new Date();
341                start();
342        }
343
344        /**
345         * Description of the Method
346         */
347        public final void reset() {
348                clear();
349                mStatus = CACHE_EMPTY;
350                mLocalFileName = null;
351        }
352
353        /**
354         * Clear the dat from this cache item
355         */
356        public final void clear() {
357                if (mLocalFileName != null && mLocalFileName.length() > 0) {
358                        File file = new File(mLocalFileName);
359
360                        if (file != null && file.exists()) {
361                                file.delete();
362                        }
363                }
364                removeAllListeners();
365        }
366
367        /**
368         * Abstract method for actually getting the data while in the thread
369         * 
370         * @return return the new status.
371         */
372        public abstract int doWork();
373
374        /**
375         * return a string representation of this datacacheobject
376         */
377        public String toString() {
378                StringBuffer sb = new StringBuffer();
379                sb.append("{");
380                sb.append("classname = " + getClass().getName() + "\n");
381                sb.append("name = " + super._name + "\n");
382                // sb.append("resname = " + (mCreatedDate != null ?
383                // mCreatedDate.getTime() : 0) + "\n");
384                sb.append("resname = " + mResourceName + "\n");
385                sb.append("date = "
386                                + (mCreatedDate != null ? mCreatedDate.getTime() : 0) + "\n");
387                sb.append("fileName = " + mLocalFileName + "\n");
388                sb.append("}");
389                return sb.toString();
390        }
391
392        // ----------------------------------------------------------------
393        // -- Runnable Interface
394        // ----------------------------------------------------------------
395        /**
396         * Description of the Method
397         */
398        public void start() {
399                if (mThread == null) {
400                        mThread = new Thread(this);
401                        mThread.setPriority(Thread.MIN_PRIORITY);
402                        mThread.setName(super._name);
403                        mThread.start();
404                }
405        }
406
407        public void stop() {
408                if (mThread != null) {
409                        mThread.interrupt();
410                }
411        }
412
413        /**
414         */
415        public void run() {
416                if (!isReady()) {
417                        mStatus = CACHE_BUSY;
418
419                        if (mThread.isInterrupted()) {
420                                mStatus = CACHE_ERROR;
421                        } else {
422                                mStatus = doWork();
423                        }
424                }
425                if (mThread.isInterrupted()) {
426                        mStatus = CACHE_ERROR;
427                }
428
429                log.debug("run - Done With Work.");
430                notifyListeners();
431
432                try {
433                        CacheManager.getInstance().updateObject(this);
434                } catch (CacheException e) {
435                        log.error("CacheException occurred during run", e);
436                }
437                mThread = null;
438        }
439
440        /**
441         * Custom deserialization method. Need to initialize mListeners to empty
442         * vector.
443         * 
444         * @param ois
445         * @throws IOException
446         * @throws ClassNotFoundException
447         */
448        private void readObject(ObjectInputStream ois) throws IOException,
449                        ClassNotFoundException {
450                ois.defaultReadObject();
451                mListeners = new Vector();
452        }
453
454}