001/*
002 * Copyright (c) 2009-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: aschultz $'
006 * '$Date: 2011-03-02 19:59:11 +0000 (Wed, 02 Mar 2011) $' 
007 * '$Revision: 27233 $'
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;
031
032import java.util.ArrayList;
033import java.util.Collections;
034import java.util.HashMap;
035import java.util.Iterator;
036import java.util.Map;
037import java.util.Vector;
038
039import org.apache.commons.logging.Log;
040import org.apache.commons.logging.LogFactory;
041import org.kepler.moml.NamedObjId;
042import org.kepler.objectmanager.cache.CacheException;
043import org.kepler.objectmanager.cache.CacheManager;
044import org.kepler.objectmanager.cache.CacheObject;
045import org.kepler.objectmanager.cache.CacheObjectInterface;
046import org.kepler.objectmanager.lsid.KeplerLSID;
047
048import ptolemy.kernel.CompositeEntity;
049import ptolemy.kernel.util.NamedObj;
050
051/**
052 * The ObjectManager searches through all of the NamedObj Objects in the
053 * workspace to see if any of them have a KeplerLSID associated with them.
054 * 
055 * @author Aaron Schultz
056 * @version $Id: ObjectManager.java 27233 2011-03-02 19:59:11Z aschultz $
057 * 
058 */
059public class ObjectManager {
060
061        private static final Log log = LogFactory.getLog(ObjectManager.class
062                        .getName());
063        private static final boolean isDebugging = log.isDebugEnabled();
064
065        // WeakHashMap will discard items required during KAR save process
066        // (refactored so this is probably only the workflow now).
067        // see: http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5200
068        //private WeakHashMap<KeplerLSID, NamedObj> _namedObjs;
069        private Map<KeplerLSID, NamedObj> _namedObjs;
070
071        /**
072         * Empty constructor
073         */
074        public ObjectManager() {
075                //_namedObjs = new WeakHashMap<KeplerLSID, NamedObj>(1);
076                HashMap<KeplerLSID, NamedObj> hashMap = new HashMap<KeplerLSID, NamedObj>(1);
077                _namedObjs = Collections.synchronizedMap(hashMap);
078        }
079
080        /**
081         * Add a NamedObject to the ObjectManager. These are searched when getting
082         * an object by LSID.
083         * 
084         * @param namedObj
085         * @throws Exception
086         */
087        public void addNamedObj(NamedObj namedObj) throws Exception {
088                KeplerLSID lsid = NamedObjId.getIdFor(namedObj);
089                //Set<KeplerLSID> keys = _namedObjs.keySet();
090                //Collection<NamedObj> values = _namedObjs.values();
091                if (_namedObjs.containsKey(lsid)) {
092                        _namedObjs.remove(lsid);
093                }
094                _namedObjs.put(lsid, namedObj);
095        }
096
097
098        /**
099         * Attempt to remove NamedObj from ObjectManager: 
100         * if getIdFor(namedObj) returns an LSID for an object 
101         * in the ObjectManager, it will be removed.
102         * 
103         * @param namedObj
104         * @return removed NamedObj, or null if none.
105         */
106        public NamedObj removeNamedObj(NamedObj namedObj) {
107                KeplerLSID lsid = NamedObjId.getIdFor(namedObj);
108                return _namedObjs.remove(lsid);
109        }
110        
111        /**
112         * Attempt to remove NamedObj from ObjectManager: 
113         * if getIdFor(namedObj) returns an LSID for an object 
114         * in the ObjectManager, it will be removed.
115         * 
116         * @param namedObj
117         * @return removed NamedObj, or null if none.
118         */
119        public ArrayList<NamedObj> removeNamedObjs(NamedObj namedObj) {
120                KeplerLSID lsid = NamedObjId.getIdFor(namedObj);
121                //it is modified because of bug // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5095#c14
122                // the reason is because the lsid of an updated namedObj is changed, the old method won't remove the old versions of namedObj.
123                //return _namedObjs.remove(lsid);
124                return removeObjectsForLSIDWithoutRevision(lsid.toStringWithoutRevision());
125        }
126
127        /**
128         * Attempts to remove object from ObjectManager: 
129         * if getObjectRevision(lsid) returns a NamedObj, calls removeNamedObj on
130         * this NamedObj.
131         * 
132         * NOTE: Chances are good this won't remove the NamedObj you want.
133         * 
134         * @param lsid
135         * @return removed NamedObj, or null if none.
136         * @throws Exception
137         */
138        public NamedObj removeObject(KeplerLSID lsid) throws Exception {
139                if (isDebugging)
140                        log.debug("removeObject(" + lsid + ")");
141                NamedObj no = getObjectRevision(lsid);
142                if (no != null) {
143                        return removeNamedObj(no);
144                }
145                return null;
146        }
147
148        /**
149         * @param lsid for the NamedObj to remove from ObjectManager
150         * @return the NamedObj removed, or null if none.
151         */
152        public NamedObj removeObjectForLSID(KeplerLSID lsid){
153                return _namedObjs.remove(lsid);
154        }
155        
156        /**
157         * Remove all NamedObjects from ObjectManager that match this lsid without
158         * revision.
159         * @param lsidWithoutRevision
160         * @return ArrayList of NamedObjs that were removed.
161         */
162        public ArrayList<NamedObj> removeObjectsForLSIDWithoutRevision(String lsidWithoutRevision){
163                ArrayList<NamedObj> removedNamedObjects = new ArrayList<NamedObj>();
164                Iterator<KeplerLSID> lsidItr = _namedObjs.keySet().iterator();
165                ArrayList<KeplerLSID> lsidsToRemove = new ArrayList<KeplerLSID>();
166                while (lsidItr.hasNext()){
167                        KeplerLSID lsid = lsidItr.next();
168                        if (lsid.toStringWithoutRevision().equals(lsidWithoutRevision)){
169                                lsidsToRemove.add(lsid);
170                        }
171                }
172                
173                lsidItr = lsidsToRemove.iterator();
174                while(lsidItr.hasNext()){
175                        KeplerLSID lsid = lsidItr.next();
176                        NamedObj removedNamedObj = _namedObjs.remove(lsid);
177                        removedNamedObjects.add(removedNamedObj);
178                }
179                
180                return removedNamedObjects;
181        }
182
183        /**
184         * 
185         * @param lsid
186         * @return
187         * @throws Exception
188         */
189        public String getObjectType(KeplerLSID lsid) throws Exception {
190                if (isDebugging)
191                        log.debug("getObjectType(" + lsid + ")");
192                NamedObj no = getObjectRevision(lsid);
193                if (no == null) {
194                        return null;
195                }
196                return no.getClass().getName();
197        }
198
199        /**
200         * 
201         * @param lsid
202         * @return
203         * @throws Exception
204         */
205        public NamedObj getObjectRevision(KeplerLSID lsid) throws Exception {
206                if (isDebugging) {
207                        log.debug("getObjectRevision(" + lsid + ")");
208                        printDebugInfo();
209                }
210
211                boolean matchRevision = true;
212
213                NamedObj obj = getObjectFromManager(lsid, matchRevision);
214                if (obj == null) {
215                        if (isDebugging)
216                                log.debug(lsid + " is not registered with ObjectManager");
217                        obj = getObjectFromCache(lsid);
218                        if (obj == null) {
219                                if (isDebugging)
220                                        log.debug(lsid + " is not registered with CacheManager");
221                        }
222                }
223                
224                if (obj != null) {
225                        if (isDebugging)
226                                log.debug(obj.getName() + " was found");
227                        // Make sure it has the NamedObjId Attribute
228                        NamedObjId noi = NamedObjId.getIdAttributeFor(obj);
229                        if (noi == null) {
230                                NamedObjId lsidSA = new NamedObjId(obj, NamedObjId.NAME);
231                                lsidSA.setExpression(lsid.toString());
232                        } else {
233                                KeplerLSID lsidCheck = noi.getId();
234                                if (!lsidCheck.equals(lsid)) {
235                                        log.error("lsids don't match: " + lsid.toString() + " != " + lsidCheck);
236                                        noi.setExpression(lsid.toString());
237                                }
238                        }
239                }
240                
241                return obj;
242        }
243
244        /**
245         * Return the NamedObj that has the highest revision number for a given LSID
246         * after searching through all the NamedObjs accessible by the
247         * ObjectManager. If no NamedObj is found at all then try to get the highest
248         * revision from the cache. If that doesn't find anything then return null.
249         * This method will replace the existing getObject(KeplerLSID) method
250         * 
251         * @param lsid
252         * @return
253         * @throws Exception
254         */
255        public NamedObj getHighestObjectRevision(KeplerLSID lsid) throws Exception {
256                boolean matchRevision = false;
257
258                Vector<NamedObj> allObjs = new Vector<NamedObj>();
259
260                for (NamedObj namedObj : _namedObjs.values()) {
261                        if (namedObj instanceof CompositeEntity) {
262                                Vector<NamedObj> theNOS = findAll(lsid,
263                                                (CompositeEntity) namedObj, matchRevision);
264
265                                if (theNOS != null) {
266                                        for (NamedObj no : theNOS) {
267                                                allObjs.add(no);
268                                        }
269                                }
270                        } else {
271                                if (NamedObjId.idMatches(lsid, namedObj, matchRevision)) {
272                                        allObjs.add(namedObj);
273                                }
274                        }
275                }
276
277                NamedObj noWithHighestLSID = null;
278                for (NamedObj no : allObjs) {
279                        if (noWithHighestLSID == null) {
280                                noWithHighestLSID = no;
281                        } else {
282                                NamedObjId idAtt = NamedObjId.getIdAttributeFor(no);
283                                if (idAtt != null) {
284                                        KeplerLSID objId = idAtt.getId();
285                                        
286                                        NamedObjId noWithHighestIdAtt = NamedObjId.getIdAttributeFor(noWithHighestLSID);
287                                        KeplerLSID noWithHighestObjId = noWithHighestIdAtt.getId();
288                                        
289                                        if (objId.getRevision() > noWithHighestObjId.getRevision()) {
290                                                noWithHighestLSID = no;
291                                        }
292                                }
293                        }
294                }
295
296                if (noWithHighestLSID == null) {
297                        // get the highest revision from the CacheManager
298                        try {
299                                CacheObject co = CacheManager.getInstance().getHighestCacheObjectRevision(lsid);
300                                if (co != null){
301                                        Object o = co.getObject();
302                                        if (o instanceof NamedObj) {
303                                                noWithHighestLSID = (NamedObj) o;
304                                        }
305                                }
306                        } catch (CacheException ce) {
307                                ce.printStackTrace();
308                        }
309
310                }
311
312                return noWithHighestLSID;
313        }
314
315        /**
316         * Return all of the NamedObjs contained by the given CompositeEntity that
317         * match the given lsid either with or without matching the revision as
318         * specified.
319         * 
320         * @param lsid
321         * @param composite
322         * @param matchRevision
323         * @return
324         */
325        private Vector<NamedObj> findAll(KeplerLSID lsid,
326                        CompositeEntity composite, boolean matchRevision) {
327                Vector<NamedObj> objs = new Vector<NamedObj>(3);
328
329                if (NamedObjId.idMatches(lsid, (NamedObj) composite, matchRevision)) {
330                        objs.add((NamedObj) composite);
331                }
332
333                for (Object obj : composite.entityList()) {
334                        if (obj instanceof NamedObj) {
335                                if (NamedObjId.idMatches(lsid, (NamedObj) obj, matchRevision)) {
336                                        objs.add((NamedObj) obj);
337                                }
338                                if (obj instanceof CompositeEntity) {
339                                        Vector<NamedObj> childObjs = findAll(lsid,
340                                                        (CompositeEntity) obj, matchRevision);
341                                        if (childObjs != null && childObjs.size() > 0) {
342                                                for (NamedObj o : childObjs) {
343                                                        objs.add(o);
344                                                }
345                                        }
346                                }
347                        }
348                }
349
350                if (objs.size() > 0) {
351                        return objs;
352                }
353                return null;
354        }
355
356        /**
357         * Return a NamedObj that has been added to the ObjectManager.
358         * 
359         * @param lsid
360         * @return
361         * @throws Exception
362         */
363        private NamedObj getObjectFromManager(KeplerLSID lsid, boolean matchRevision)
364                        throws Exception {
365                if (isDebugging)
366                        log.debug("getObjectFromManager(" + lsid + "," + matchRevision
367                                        + ")");
368                for (NamedObj namedObj : _namedObjs.values()) {
369                        if (namedObj instanceof CompositeEntity) {
370                                NamedObj theNO = checkComposite(lsid,
371                                                (CompositeEntity) namedObj, matchRevision);
372
373                                if (theNO != null) {
374                                        if (isDebugging)
375                                                log.debug("Found NamedObj: " + theNO.getName() + " "
376                                                                + NamedObjId.getIdFor(theNO));
377                                        return theNO;
378                                }
379                        } else {
380                                if (NamedObjId.idMatches(lsid, namedObj, matchRevision)) {
381                                        return namedObj;
382                                }
383                        }
384                }
385                return null;
386        }
387
388        /**
389         * Return a NamedObj that has been instantiated from the CacheManager.
390         * 
391         * @param lsid
392         * @return
393         * @throws Exception
394         */
395        private NamedObj getObjectFromCache(KeplerLSID lsid) throws Exception {
396                NamedObj obj = null;
397
398                CacheObjectInterface coi = CacheManager.getInstance().getObject(lsid);
399                if (coi != null) {
400                        if (isDebugging)
401                                log.debug("CacheObjectInterface found in CacheManager");
402                        Object o = coi.getObject();
403                        if (o != null) {
404                                if (isDebugging) {
405                                        log.debug(o.getClass().getName());
406                                }
407                                if (o instanceof NamedObj) {
408                                        obj = (NamedObj) o;
409                                } else if (o instanceof ActorMetadata) {
410                                        ActorMetadata am = (ActorMetadata) o;
411                                        obj = am.getActor();
412                                }
413                        }
414                }
415                return obj;
416
417        }
418
419        /**
420         * Deeply recurse all entities of the given composite entity and return the
421         * NamedObj that matches the search LSID
422         */
423        private NamedObj checkComposite(KeplerLSID lsid, CompositeEntity composite,
424                        boolean matchRevision) {
425                if (isDebugging)
426                        log.debug("checkComposite(" + composite.getName() + ")");
427
428                if (NamedObjId.idMatches(lsid, (NamedObj) composite, matchRevision)) {
429                        return (NamedObj) composite;
430                }
431
432                for (Object obj : composite.entityList()) {
433                        if (obj instanceof NamedObj) {
434                                if (isDebugging)
435                                        log.debug(((NamedObj) obj).getName());
436                                if (NamedObjId.idMatches(lsid, (NamedObj) obj, matchRevision)) {
437                                        return (NamedObj) obj;
438                                }
439                                if (obj instanceof CompositeEntity) {
440                                        NamedObj theObj = checkComposite(lsid,
441                                                        (CompositeEntity) obj, matchRevision);
442                                        if (theObj != null) {
443                                                return theObj;
444                                        }
445                                }
446                        }
447                }
448                return null;
449        }
450
451        /**
452         * Print the objects that are currently registered with the ObjectManager.
453         */
454        public void printDebugInfo() {
455                System.out.println("*************"
456                                + " NamedObjs registered with ObjectManager:");
457                Iterator<KeplerLSID> lsidItr = _namedObjs.keySet().iterator();
458                while (lsidItr.hasNext()){
459                        KeplerLSID lsid = lsidItr.next();
460                        System.out.println(_namedObjs.get(lsid).getName()+"=>"+lsid);
461                }
462                
463                System.out.println("** getIdFor these NamedObj returns:");
464                for (NamedObj no : _namedObjs.values()) {
465                        System.out.println(no.getName() + " getIdFor("+no.getName()+"):" + NamedObjId.getIdFor(no));
466                }
467
468                System.out.println("*************\n");
469
470        }
471
472        /**
473         * Method for getting an instance of this singleton class.
474         */
475        public static ObjectManager getInstance() {
476                return ObjectManagerHolder.INSTANCE;
477        }
478
479        private static class ObjectManagerHolder {
480                private static final ObjectManager INSTANCE = new ObjectManager();
481        }
482}