001/*
002 *  Copyright (c) 2003-2010 The Regents of the University of California.
003 *  All rights reserved.
004 *  Permission is hereby granted, without written agreement and without
005 *  license or royalty fees, to use, copy, modify, and distribute this
006 *  software and its documentation for any purpose, provided that the above
007 *  copyright notice and the following two paragraphs appear in all copies
008 *  of this software.
009 *  IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
010 *  FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
011 *  ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
012 *  THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
013 *  SUCH DAMAGE.
014 *  THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
015 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
016 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
017 *  PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
018 *  CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
019 *  ENHANCEMENTS, OR MODIFICATIONS.
020 *  PT_COPYRIGHT_VERSION_2
021 *  COPYRIGHTENDKEY
022 */
023package org.kepler.kar;
024
025import java.io.File;
026import java.io.IOException;
027import java.io.Serializable;
028import java.util.ArrayList;
029import java.util.Enumeration;
030import java.util.Hashtable;
031import java.util.Iterator;
032import java.util.List;
033import java.util.Map;
034import java.util.Set;
035import java.util.Vector;
036import java.util.jar.Attributes;
037import java.util.jar.Attributes.Name;
038import java.util.jar.JarEntry;
039import java.util.jar.JarFile;
040import java.util.zip.ZipEntry;
041
042import org.apache.commons.logging.Log;
043import org.apache.commons.logging.LogFactory;
044import org.kepler.build.modules.Module;
045import org.kepler.build.util.Version;
046import org.kepler.configuration.ConfigurationManager;
047import org.kepler.configuration.ConfigurationManagerException;
048import org.kepler.configuration.ConfigurationProperty;
049import org.kepler.configuration.NamespaceException;
050import org.kepler.objectmanager.cache.CacheException;
051import org.kepler.objectmanager.cache.CacheManager;
052import org.kepler.objectmanager.cache.CacheObject;
053import org.kepler.objectmanager.cache.LocalRepositoryManager;
054import org.kepler.objectmanager.lsid.KeplerLSID;
055
056import ptolemy.actor.gui.TableauFrame;
057
058/**
059 * A KAR file is just a jar file that requires a manifest to be present that has
060 * "KAR-Version" and "lsid" main attributes. This KARFile is used for reading
061 * KAR files. For Writing KARFiles see KARBuilder class.
062 * 
063 * @author Aaron Schultz
064 */
065public class KARFile extends JarFile implements Serializable {
066        private static final long serialVersionUID = 1522142700198331341L;
067        private static final Log log = LogFactory.getLog(KARFile.class.getName());
068        private static final boolean isDebugging = log.isDebugEnabled();
069
070        /**
071         * This is the current KAR version.
072         */
073        public static final String VERSION_2_1 = "2.1";
074        public static final String CURRENT_VERSION = VERSION_2_1;
075        
076        /**
077         * Old versions still supported.
078         */
079        public static final String VERSION_2_0 = "2.0";
080        public static final String VERSION_1_0 = "1.0";
081        
082        //TODO these variables might be better placed elsewhere
083        /** "strict"*/
084        public static final String KAR_COMPLIANCE_STRICT = "strict";
085        /** "relaxed"*/
086        public static final String KAR_COMPLIANCE_RELAXED = "relaxed";
087        /** "relaxed" if you change this you should also change KARFILE_CONFIG_PROP_MODULE's configuration.xml
088         we need to keep both for users that started with an earlier version of configuration.xml
089         that lacks the KAR_COMPLIANCE_PROPERTY
090         */
091        public static final String KAR_COMPLIANCE_DEFAULT = KAR_COMPLIANCE_RELAXED;
092        /** "KARComplianceMode"*/
093        public static final String KAR_COMPLIANCE_PROPERTY_NAME = "KARComplianceMode";
094        /** "core" Module*/
095        public static final Module KARFILE_CONFIG_PROP_MODULE = ConfigurationManager.getModule("core");
096
097        /** "karVersions"*/
098        public static final String KAR_VERSIONS_PROPERTY_NAME = "karVersions";
099        /** "currentVersion" sub-element of karVersions*/
100        public static final String KAR_CURRENT_VERSION_PROPERTY_NAME = "currentVersion";
101        /** "karVersion" sub-element of karVersions*/
102        public static final String KAR_VERSION_PROPERTY_NAME = "karVersion";
103        /** "version" sub-element of karVersion*/
104        public static final String KAR_VERSION_VERSION_PROPERTY_NAME = "version";
105        /** "namespace" sub-element of karVersion*/
106        public static final String KAR_VERSION_NAMESPACE_PROPERTY_NAME = "namespace";
107        /** "schemaUrl" sub-element of karVersion*/
108        public static final String KAR_VERSION_SCHEMAURL_PROPERTY_NAME = "schemaUrl";
109        /** "resourceDir" sub-element of karVersion*/
110        public static final String KAR_VERSION_RESOURCEDIR_PROPERTY_NAME = "resourceDir";
111        /** "resourceFileName" sub-element of karVersion*/
112        public static final String KAR_VERSION_RESOURCE_FILENAME_PROPERTY_NAME = "resourceFileName";
113        
114        //if you change any of these you must also change KARFILE_CONFIG_PROP_MODULE's configuration.xml
115        public static final String KAR_CURRENT_VERSION_DEFAULT = "2.1";
116        public static final String KAR_VERSION_200_RESOURCEDIR_DEFAULT = "kar-2.0.0";
117        public static final String KAR_VERSION_200_RESOURCE_FILENAME_DEFAULT = "KARFile.xsd";
118        public static final String KAR_VERSION_200_VERSION_DEFAULT = "2.0";
119        public static final String KAR_VERSION_200_NAMESPACE_DEFAULT = "http://www.kepler-project.org/kar-2.0.0";
120        public static final String KAR_VERSION_200_SCHEMAURL_DEFAULT = "https://code.kepler-project.org/code/kepler/branches/releases/release-branches/core-2.0/resources/KARFile.xsd";
121        public static final String KAR_VERSION_210_RESOURCEDIR_DEFAULT = "kar-2.1.0";
122        public static final String KAR_VERSION_210_RESOURCE_FILENAME_DEFAULT = "KARFile-2.1.0.xsd";
123        public static final String KAR_VERSION_210_VERSION_DEFAULT = "2.1";
124        public static final String KAR_VERSION_210_NAMESPACE_DEFAULT = "http://www.kepler-project.org/kar-2.1.0";
125        public static final String KAR_VERSION_210_SCHEMAURL_DEFAULT = "https://code.kepler-project.org/code/kepler/branches/releases/release-branches/core-2.1/resources/KARFile-2.1.0.xsd";
126
127        /**
128         * This is the File extension to be used for KAR files.
129         */
130        public static final String EXTENSION = "kar";
131
132        /**
133         * This is the list of all supported versions.
134         */
135        private Vector<String> _supportedVersions = new Vector<String>();
136
137        /**
138         * <code>Name</code> object for <code>lsid</code> manifest attribute used
139         * for globally identifying KAREntries as unique.
140         */
141        public static final Name LSID = new Name("lsid");
142
143        /**
144         * <code>Name</code> object for <code>KAR-Version</code> manifest attribute.
145         * This attribute indicates the version number of the manifest standard to
146         * which a KAR file's manifest conforms.
147         */
148        public static final Name KAR_VERSION = new Name("KAR-Version");
149
150        /**
151         * <code>Name</code> object for <code>MOD_DEPEND</code> manifest attribute.
152         * This attribute indicates modules that are needed in order to open this
153         * KAR file.
154         */
155        public static final Name MOD_DEPEND = new Name("module-dependencies");
156        
157        /**
158         * <code>Name</code> object for <code>OPENABLE</code> manifest attribute.
159         * This attribute indicates whether or not this KAR file is openable.
160         */
161        public static final Name OPENABLE = new Name("openable");
162
163        /**
164         * Separator to be used for dependency lists.
165         */
166        public static final String DEP_SEPARATOR = ";";
167
168        /**
169         * If the KARFile object was created from a KARFile on disk this is the File
170         * path and name of that file.
171         */
172        private File _fileOnDisk;
173
174        /**
175         * A list of all the entry types contained in this KARFile indexed by LSID.
176         */
177        private Hashtable<KeplerLSID, String> _lsidTypes = new Hashtable<KeplerLSID, String>();
178
179        /**
180         * A list of all the entry names contained in this KARFile indexed by LSID.
181         */
182        private Hashtable<KeplerLSID, String> _lsidNames = new Hashtable<KeplerLSID, String>();
183
184        /**
185         * Constructor for creating a KARFile from an existing file.
186         * 
187         * @param f
188         *            the file to create the KAR from
189         * @throws IOException
190         */
191        public KARFile(File f) throws IOException {
192                super(f);
193                if (isDebugging)
194                        log.debug("KARFile(" + f + ")");
195
196                _fileOnDisk = f;
197
198                initializeSupportedVersions();
199
200                if (getManifest() == null) {
201                        throw new IOException("This KAR file does not contain a Manifest.");
202                }
203
204                String lsidStr = getManifest().getMainAttributes().getValue(LSID);
205                if (isDebugging)
206                        log.debug(lsidStr);
207                if (lsidStr == null) {
208                        throw new IOException(
209                                        "The KAR file does not contain an lsid attribute in the manifest");
210                }
211                if (!KeplerLSID.isKeplerLSIDFormat(lsidStr)) {
212                        throw new IOException(
213                                        "The LSID of the KAR file is not properly formatted.");
214                }
215
216                String version = getManifest().getMainAttributes()
217                                .getValue(KAR_VERSION);
218                if (version == null) {
219                        throw new IOException(
220                                        "The KAR file does not contain a KAR-Version attribute in the manifest.");
221                }
222                if (!_supportedVersions.contains(version)) {
223                        throw new IOException(version
224                                        + " is not a supported KAR version.\n"
225                                        + getSupportedVersionString());
226                }
227
228                Enumeration<JarEntry> karFileEnum = entries();
229                while (karFileEnum.hasMoreElements()) {
230                        JarEntry entry = (JarEntry) karFileEnum.nextElement();
231                        KeplerLSID lsid = getEntryLSID(entry);
232                        String type = getEntryType(entry);
233                        if (lsid != null && type != null) {
234                                _lsidNames.put(lsid, entry.getName());
235                                _lsidTypes.put(lsid, type);
236                        }
237                }
238
239        }
240
241        private void initializeSupportedVersions() {
242                _supportedVersions.add(VERSION_1_0);
243                _supportedVersions.add(VERSION_2_0);
244                _supportedVersions.add(VERSION_2_1);
245        }
246
247        public KeplerLSID getLSID() {
248                try {
249                        return new KeplerLSID(getManifest().getMainAttributes().getValue(
250                                        LSID));
251                } catch (Exception e) {
252                        return null;
253                }
254        }
255
256        /**
257         * @param lsid
258         * @throws IOException
259         */
260        public void setLSID(KeplerLSID lsid) throws IOException {
261                Attributes atts = getManifest().getMainAttributes();
262                if (atts.containsKey(LSID)) {
263                        atts.remove(LSID);
264                }
265                atts.put(LSID, lsid.toString());
266        }
267
268        public String getKARVersion() {
269                try {
270                        Attributes atts = getManifest().getMainAttributes();
271                        String karVersion = atts.getValue(KAR_VERSION);
272                        return karVersion;
273                } catch (IOException e) {
274                        return null;
275                }
276        }
277        
278        public boolean isOpenable() {
279                try {
280                        Attributes atts = getManifest().getMainAttributes();
281                        String openable = atts.getValue(OPENABLE);
282                        if (openable != null) {
283                                openable = openable.trim();
284                                if (openable.equalsIgnoreCase("false")) {
285                                        return false;
286                                }
287                        }
288                } catch (IOException e) {
289                        e.printStackTrace();
290                }
291                Vector<String>moduleDependencies = getModuleDependencies();
292                if (ModuleDependencyUtil.checkIfModuleDependenciesSatisfied(moduleDependencies)){
293                        return true;
294                }
295                return false;
296        }
297
298        /**
299         * Set the version of the KARFile.
300         * 
301         * @param version
302         * @throws IOException
303         *             if the given version is not supported
304         */
305        public void setVersion(String version) throws IOException {
306                if (_supportedVersions.contains(version)) {
307                        Attributes atts = getManifest().getMainAttributes();
308                        if (atts.containsKey(KAR_VERSION)) {
309                                atts.remove(KAR_VERSION);
310                        }
311                        atts.put(KAR_VERSION, version);
312                } else {
313                        throw new IOException(version
314                                        + " is not a supported KAR version.\n"
315                                        + getSupportedVersionString());
316                }
317        }
318
319        public Vector<String> getModuleDependencies() {
320                Vector<String> dependencies = new Vector<String>();
321                try {
322                        String depStr = getManifest().getMainAttributes().getValue(
323                                        MOD_DEPEND);
324                        if (depStr == null) {
325                                return dependencies;
326                        }
327                        dependencies = ModuleDependencyUtil.parseDependencyString(depStr);
328                } catch (Exception e) {
329                }
330                return dependencies;
331        }
332
333        /**
334         * Return the LSIDs of all of the entries contained by this KARFile.
335         * 
336         * */
337        public Set<KeplerLSID> getContainedLSIDs() {
338                return _lsidTypes.keySet();
339        }
340
341        public String getEntryType(KeplerLSID lsid) {
342                return _lsidTypes.get(lsid);
343        }
344
345        public String getEntryName(KeplerLSID lsid) {
346                return _lsidNames.get(lsid);
347        }
348
349        /**
350         * Return the name of the local repository that this KAR is stored in.
351         * 
352         * @return null if no name is found
353         */
354        public String getLocalRepoName() {
355                LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
356                LocalRepositoryManager.LocalRepository repository = lrm.getContainingLocalRepository(getFileLocation());
357                if(repository != null) {
358                    return lrm.getLocalRepositories().get(repository);
359                }
360                return null;
361        }
362
363        /**
364         * Return the path where this kar file is found on disk.
365         */
366        public String getPath() {
367                String filePath = getFileLocation().getPath();
368                String fileName = getFileLocation().getName();
369                if (isDebugging)
370                        log.debug(filePath + "   " + fileName);
371
372                if (filePath.endsWith(fileName)) {
373                        // remove the filename
374                        int i = filePath.length() - fileName.length();
375                        if (i > 0) {
376                                filePath = filePath.substring(0, i);
377                        }
378                        if (isDebugging)
379                                log.debug("New filePath: " + filePath);
380                }
381                return filePath;
382        }
383
384        public File getFileLocation() {
385                return _fileOnDisk;
386        }
387
388        /**
389         * returns a KAREntry for the given lsid. If there is not KAREntry then
390         * return null.
391         * 
392         * @param name
393         *            the name of the entry to return
394         */
395        public ZipEntry getKAREntry(KeplerLSID lsid) {
396                String path = _lsidNames.get(lsid);
397                ZipEntry ze = super.getEntry(path);
398                if (ze != null) {
399                        return ze;
400                }
401                return null;
402        }
403
404        /**
405         * Returns only valid KAREntries contained in this KARFile. To get all of
406         * the JarEntries use the entries() method.
407         * 
408         * @return List<KAREntry> of all valid KAREntries in this KARFile.
409         */
410        public List<KAREntry> karEntries() {
411                if (isDebugging)
412                        log.debug("karEntries()");
413                Enumeration<JarEntry> entries = entries();
414                Vector<KAREntry> karEntries = new Vector<KAREntry>();
415                while (entries.hasMoreElements()) {
416                        JarEntry entry = entries.nextElement();
417                        if (isDebugging)
418                                log.debug(entry.getName());
419                        if (entry.getName().equals("MANIFEST.MF")
420                                        || entry.getName().equals("META-INF/")
421                                        || entry.getName().equals(JarFile.MANIFEST_NAME)) {
422                                // skip manifest entries
423                        } else {
424                                KAREntry karEntry = new KAREntry(entry);
425                                if (karEntry.isValid()) {
426                                        if (isDebugging)
427                                                log.debug("karEntry.isValid() == true");
428                                        karEntries.add(karEntry);
429                                } else {
430                                        if (isDebugging)
431                                                log.debug("karEntry.isValid() == false");
432                                }
433                        }
434                }
435                return karEntries;
436        }
437
438        /**
439         * Return an array of lsids of a certain type from within this kar. if the
440         * type is null return all of the lsids
441         * 
442         * @param getType
443         *            the type of the content to get
444         */
445        public KeplerLSID[] getContentOfType(String getType) throws Exception {
446
447                Vector<KeplerLSID> returnVec = new Vector<KeplerLSID>();
448                for (KeplerLSID lsid : this.getContainedLSIDs()) {
449                        String type = this.getEntryType(lsid);
450                        if (getType == null || getType.equals(type)) {
451                                returnVec.addElement(lsid);
452                        } else if (isSubclass(getType, type)) {
453                                returnVec.addElement(lsid);
454                        }
455                }
456
457                KeplerLSID[] lsids = new KeplerLSID[returnVec.size()];
458                for (int i = 0; i < returnVec.size(); i++) {
459                        lsids[i] = (KeplerLSID) returnVec.elementAt(i);
460                }
461                return lsids;
462        }
463
464        /**
465         * Determine if type is a subclass of getType. This belongs in a UTIL class
466         * somewhere?
467         * 
468         * @param getType
469         * @param type
470         * @return
471         * @throws ClassNotFoundException
472         */
473        public static boolean isSubclass(String getType, String type)
474                        throws ClassNotFoundException {
475                
476                // protect against nulls
477                if (getType == null || type == null)
478                        return false;
479
480                // the superclass test
481                Class getClazz = Class.forName(getType);
482
483                // handle superclasses
484                Class theClazz = Class.forName(type);
485                Class superClazz = theClazz.getSuperclass();
486                while (superClazz != null) {
487                        if (superClazz.getName().equals(getClazz.getName())) {
488                                return true;
489                        }
490                        superClazz = superClazz.getSuperclass();
491                }
492                return false;
493        }
494
495        /**
496         * @return String of the supported KAR versions.
497         */
498        private String getSupportedVersionString() {
499                String s = "Supported KAR versions:\n";
500                for (String v : _supportedVersions) {
501                        s += "      " + v + "\n";
502                }
503                return s;
504        }
505
506        /**
507         * Read the LSID from the attributes of the given entry and return it as a
508         * KeplerLSID.
509         * 
510         * @param entry
511         * @return
512         * @throws IOException
513         */
514        private KeplerLSID getEntryLSID(JarEntry entry) throws IOException {
515                Attributes atts = entry.getAttributes();
516                if (atts == null) {
517                        return null;
518                }
519
520                String lsidStr = atts.getValue(KAREntry.LSID);
521                if (lsidStr == null) {
522                        log.error("KAREntries must have an LSID and a type.");
523                        return null;
524                }
525
526                KeplerLSID lsid = null;
527                try {
528                        lsid = new KeplerLSID(lsidStr);
529                } catch (Exception e) {
530                        log.warn(lsidStr + " is not a properly formatted KeplerLSID");
531                        return null;
532                }
533                return lsid;
534        }
535
536        /**
537         * Read the type attribute from the given entry and return it as a string.
538         * 
539         * @param entry
540         * @return
541         * @throws IOException
542         */
543        private String getEntryType(JarEntry entry) throws IOException {
544                Attributes atts = entry.getAttributes();
545                if (atts == null) {
546                        return null;
547                }
548                String type = atts.getValue(KAREntry.TYPE);
549                return type;
550        }
551
552        /**
553         * This method makes sure that all of the entries of this KARFile are in the
554         * Cache. It caches the entries in the order that their dependencies
555         * dictate.
556         * 
557         * @throws Exception
558         */
559        public void cacheKARContents() throws Exception {
560                if (isDebugging) {
561                        log.debug("openKAR: " + this.toString());
562                }
563                try {
564                        // get references to all the managers we'll be using
565                        LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
566                        KARCacheManager kcm = KARCacheManager.getInstance();
567                        CacheManager cm = CacheManager.getInstance();
568
569                        // Make sure the file is in a local repository
570                        if (!lrm.isInLocalRepository(getFileLocation())) {
571                                log
572                                                .warn("KAR should be in a Local Repository Folder to be inserted in the cache: "
573                                                                + getFileLocation());
574                                // return;
575                        }
576
577                        // Add a row to the KARS_CACHED table
578                        boolean inserted = kcm.insertIntoCache(this);
579                        if (!inserted) {
580                                // This KAR has already been cached, don't do it again
581                                return;
582                        }
583
584                        // keep two lists while traversing the dependencies, start with all
585                        // of the entries (we don't know yet if they are cached or not)
586                        // and move them into the cached entries as they are cached (or if
587                        // they are already cached)
588                        Vector<KAREntry> entries = (Vector<KAREntry>) karEntries();
589                        Hashtable<KeplerLSID, KAREntry> cachedEntries = new Hashtable<KeplerLSID, KAREntry>();
590
591                        // do one pass through the entries to see if any of them are already
592                        // in the cache
593                        for (KAREntry entry : entries) {
594                                KeplerLSID lsid = entry.getLSID();
595
596                                // See if this entry is already in the Cache
597                                boolean alreadyCached = cm.isContained(lsid);
598                                if (alreadyCached) {
599
600                                        // add this entry into the cachedEntries list
601                                        cachedEntries.put(entry.getLSID(), entry);
602
603                                        // Insert a row into the KAR_CONTENTS table for this entry
604                                        File karFile = getFileLocation();
605                                        KeplerLSID entryLsid = entry.getLSID();
606                                        String entryName = entry.getName();
607                                        String entryType = entry.getType();
608                                        kcm.insertEntryIntoCache(karFile, entryLsid, entryName,
609                                                        entryType);
610                                }
611                        }
612
613                        // remove entries that were already cached
614                        for (KAREntry entry : cachedEntries.values()) {
615                                entries.remove(entry);
616                        }
617
618                        // keep cycling through the uncached entries until the list is empty
619                        while (entries.size() > 0) {
620
621                                // keep track of the entries cached during this pass
622                                Vector<KAREntry> cachedThisPass = new Vector<KAREntry>(entries
623                                                .size());
624
625                                // cycle through all of the remaining, uncached entries
626                                for (KAREntry entry : entries) {
627                                        if (isDebugging)
628                                                log.debug(entry.getName());
629
630                                        // get the dependency list for this entry
631                                        List<KeplerLSID> depList = entry.getLsidDependencies();
632
633                                        if (depList.size() == 0) {
634                                                // if there are no dependencies we just cache it
635                                                boolean success = cache(entry);
636                                                if (success) {
637                                                        cachedEntries.put(entry.getLSID(), entry);
638                                                        cachedThisPass.add(entry);
639                                                        break;
640                                                }
641                                                if (isDebugging)
642                                                        log.debug(success);
643                                        } else {
644                                                // if there are dependencies then we check to make sure
645                                                // that all of the dependencies have already been cached
646                                                boolean allDependenciesHaveBeenCached = true;
647                                                for (KeplerLSID lsid : depList) {
648                                                        // if any of the dependencies have not been cached,
649                                                        // set false
650                                                        if (!cm.isContained(lsid)) {
651                                                                allDependenciesHaveBeenCached = false;
652                                                        }
653                                                }
654                                                if (allDependenciesHaveBeenCached) {
655                                                        // all dependencies have been cached so it is
656                                                        // OK to cache this entry
657                                                        boolean success = cache(entry);
658                                                        if (success) {
659                                                                cachedEntries.put(entry.getLSID(), entry);
660                                                                cachedThisPass.add(entry);
661                                                                break;
662                                                        }
663                                                        if (isDebugging)
664                                                                log.debug(success);
665                                                }
666                                        }
667                                }
668                                if (cachedThisPass.size() == 0) {
669                                        // Bad news, nothing is getting cached
670                                        // This means that there are uncached entries that
671                                        // have unsatisfied dependencies
672                                        // break out to avoid infinite loop
673                                        // Vector<KAREntry> entriesWithBrokenDependencies = entries;
674                                        break;
675                                }
676
677                                // remove any entries that got cached this pass
678                                for (KAREntry entry : cachedThisPass) {
679                                        entries.remove(entry);
680                                }
681
682                        }
683                } catch (Exception e) {
684                        e.printStackTrace();
685                }
686
687        }
688
689        /**
690         * Here we go through all the KAREntries and call the open method of the
691         * appropriate KAREntryHandlers. It is assumed that cacheKARContents() has
692         * been called at some point before openKAR() In other words everything in
693         * the kar is already cached when calling the open() method of the
694         * KAREntryHandlers.
695         * 
696         * Note: There is some issue with having this method here, since it is
697         * really a gui specific function it probably does not belong here in the
698         * core module.
699         * 
700         * @param tableauFrame
701         * @param forceOpen
702         * @throws Exception
703         * @returns true if at least one of the entries in the KAR was opened
704         */
705        public boolean openKARContents(TableauFrame tableauFrame, boolean forceOpen) throws Exception {
706                if (isDebugging)
707                        log.debug("openKAR: " + this.toString());
708                
709                if (!forceOpen && !isOpenable()){
710                        return false;
711                }
712                
713                try {
714
715                        /**
716                         * Loop through the kar entries and call the open method of the
717                         * appropriate KAREntryHandler
718                         */
719                        Vector<KAREntry> unopenedEntries = (Vector<KAREntry>) karEntries();
720                        Hashtable<KeplerLSID, KAREntry> openedEntries = new Hashtable<KeplerLSID, KAREntry>();
721
722                        // keep cycling through the unopened entries until the list is empty
723                        while (unopenedEntries.size() > 0) {
724
725                                // keep track of the entries that were opened during this pass
726                                Vector<KAREntry> openedThisPass = new Vector<KAREntry>(
727                                                unopenedEntries.size());
728
729                                // cycle through all of the remaining, unopened entries
730                                for (KAREntry entry : unopenedEntries) {
731                                        if (isDebugging) {
732                                                log.debug(entry.getName());
733                                        }
734
735                                        // get the dependency list for this entry
736                                        List<KeplerLSID> depList = entry.getLsidDependencies();
737
738                                        if (depList.size() == 0) {
739                                                // if there are no dependencies we just open it up
740                                                boolean success = open(entry, tableauFrame);
741                                                if (success) {
742                                                        openedEntries.put(entry.getLSID(), entry);
743                                                        openedThisPass.add(entry);
744                                                        break;
745                                                }
746                                                if (isDebugging)
747                                                        log.debug(success);
748                                        } else {
749                                                // if there are dependencies then we check to make sure
750                                                // that all of the dependencies have already been opened
751                                                boolean allDependenciesHaveBeenOpened = true;
752                                                for (KeplerLSID lsid : depList) {
753                                                        // if any of the dependencies have not been opened,
754                                                        // set false
755                                                        if (!openedEntries.containsKey(lsid)) {
756                                                                allDependenciesHaveBeenOpened = false;
757                                                        }
758                                                }
759                                                if (allDependenciesHaveBeenOpened) {
760                                                        // dependencies have been opened so OK to open this
761                                                        // one
762                                                        boolean success = open(entry, tableauFrame);
763                                                        if (success) {
764                                                                openedEntries.put(entry.getLSID(), entry);
765                                                                openedThisPass.add(entry);
766                                                                break;
767                                                        }
768                                                        if (isDebugging)
769                                                                log.debug(success);
770                                                }
771                                        }
772                                }
773
774                                if (openedThisPass.size() == 0) {
775                                        // Bad news, nothing is getting opened
776                                        // break out to avoid infinite loop
777                                        break;
778                                }
779
780                                // remove the entries that were opened during this pass
781                                for (KAREntry entry : openedThisPass) {
782                                        unopenedEntries.remove(entry);
783                                }
784                        }
785                        
786
787                        if (openedEntries.size() == 0) {
788                                return false;
789                        }
790                } catch (Exception e) {
791                        throw new Exception("Error on Open: " + e.getMessage());
792                }
793                return true;
794        }
795
796        /**
797         * Get all the KAREntryHandlers that can handle the given entry.
798         * 
799         * @param entry
800         * @return KAREntryHandlers that can handle the given entry.
801         */
802        private Vector<KAREntryHandler> getHandlersForEntry(KAREntry entry) {
803                Vector<KAREntryHandler> handlers = new Vector<KAREntryHandler>(2);
804
805                // First we make sure that the EntryHandler that wrote the entry
806                // is present in the system
807                try {
808                        // check for it in the classpath
809                        Class.forName(entry.getHandler());
810                } catch (Exception e) {
811                        log.warn("KAREntryHandler not in classpath: " + entry.getHandler());
812                        return handlers;
813                }
814
815                try {
816                        // check to see if it is registered with the KAREntryHandler
817                        // extension point
818                        boolean writeHandlerPresent = false;
819                        for (KAREntryHandler k : CacheManager.getInstance()
820                                        .getKAREntryHandlers()) 
821                        {
822                                if (k.getClass().getName().equals(entry.getHandler())) {
823                                        writeHandlerPresent = true;
824                                        break;
825                                }
826                        }
827                        if (writeHandlerPresent) {
828                                // continue looking for EntryHandlers that handle this type
829                        } else {
830                                log.warn("KAREntryHandler is not registered with suite: "
831                                                + entry.getHandler());
832                                return handlers;
833                        }
834                } catch (CacheException e1) {
835                        e1.printStackTrace();
836                } catch (Exception e) {
837                }
838
839                KeplerLSID lsid = entry.getLSID();
840                String type = entry.getType();
841                if (isDebugging)
842                        log.debug(lsid + " " + type);
843                if (lsid == null || type == null) {
844                        return null;
845                }
846                try {
847                        if (getKARVersion().equals(VERSION_1_0)) {
848                                // In version 1.0 KARs the type is a one word string
849                                // that identifies what type of file it is
850                                for (KAREntryHandler k : CacheManager.getInstance()
851                                                .getKAREntryHandlers()) {
852                                        if (k.getTypeName().equals(type)) {
853                                                handlers.add(k);
854                                        }
855                                }
856                        } else if (getKARVersion().equals(VERSION_2_0) || 
857                                getKARVersion().equals(VERSION_2_1)) {
858                                // In version 2.0 and 2.1 KARs the type is the binary class
859                                // name for the type of file that the KAREntry is.
860                                // A KAREntryHandler may be able to handle more than
861                                // one type in version 2.0 and 2.1
862                                for (KAREntryHandler k : CacheManager.getInstance()
863                                                .getKAREntryHandlers()) {
864                                        if (k.handlesType(type)) {
865                                                handlers.add(k);
866                                        }
867                                }
868                        }
869                } catch (CacheException e) {
870                        e.printStackTrace();
871                }
872                return handlers;
873        }
874
875        /**
876         * Insert the object into the cache and record it in the kar contents table.
877         * 
878         * @param entry
879         * @return true if the entry could be cached
880         */
881        private boolean cache(KAREntry entry) {
882                if (entry.getAttributes() == null) {
883                        return false;
884                }
885
886                CacheManager cm;
887                KARCacheManager kcm;
888                try {
889                        cm = CacheManager.getInstance();
890                        kcm = KARCacheManager.getInstance();
891
892                        if (cm.isContained(entry.getLSID())) {
893                                // This entry is already cached
894                                // make sure there is a row for it in the KAR_CONTENTS table
895                                File karFile = getFileLocation();
896                                KeplerLSID entryLsid = entry.getLSID();
897                                String entryName = entry.getName();
898                                String entryType = entry.getType();
899                                kcm.insertEntryIntoCache(karFile, entryLsid, entryName,
900                                                entryType);
901                                return true;
902                        }
903                } catch (CacheException e1) {
904                        e1.printStackTrace();
905                        return false;
906                }
907
908                // get all KAREntryHandlers for this KAREntry
909                Vector<KAREntryHandler> handlers = getHandlersForEntry(entry);
910                if (handlers == null)
911                        return false;
912
913                if (handlers.size() <= 0) {
914                        // this KAREntry type is not recognized ignore it
915                        log.warn("No KAREntryHandlers found for KAREntry: " + entry.getName() +" ignoring this item.");
916                        return false;
917                }
918
919                // loop through all of the handlers and pass the entry to their cache
920                // method
921                // if they return a cache object then insert it into the cache.
922                for (KAREntryHandler keh : handlers) {
923                        try {
924                                CacheObject co = keh.cache(this, entry);
925                                if (co != null) {
926
927                                        // The handler returned a CacheObject, insert it into the
928                                        // cache
929                                        cm.insertObject(co);
930
931                                        // Insert a row into the KAR_CONTENTS table for this entry
932                                        File karFile = getFileLocation();
933                                        KeplerLSID entryLsid = entry.getLSID();
934                                        String entryName = entry.getName();
935                                        String entryType = entry.getType();
936                                        kcm.insertEntryIntoCache(karFile, entryLsid, entryName,
937                                                        entryType);
938
939                                }
940                        } catch (CacheException ce) {
941                                log.warn("KAREntry was not cached: " + entry.getName());
942                        } catch (Exception e) {
943                                e.printStackTrace();
944                        }
945                }
946
947                return true;
948        }
949
950        /**
951         * Call the open methods of all KAREntryHandlers that support the type of
952         * the given entry.
953         * 
954         * @param entry
955         * @param tableauFrame
956         * @return
957         */
958        public boolean open(KAREntry entry, TableauFrame tableauFrame) {
959                Vector<KAREntryHandler> handlers = getHandlersForEntry(entry);
960                if (handlers == null)
961                        return false;
962
963                if (handlers.size() <= 0) {
964                        // this KAREntry type is not recognized ignore it
965                        log.warn("No KAREntryHandlers found for KAREntry: " + entry.getName() +" ignoring this item.");
966                        return false;
967                }
968
969                boolean allsuccess = true;
970                for (KAREntryHandler keh : handlers) {
971                        try {
972                                boolean success = keh.open(this, entry, tableauFrame);
973                                if (!success) {
974                                        allsuccess = false;
975                                }
976                                if (isDebugging)
977                                        log.debug(success);
978                        } catch (Exception e) {
979                                e.printStackTrace();
980                        }
981                }
982                return allsuccess;
983
984        }
985        
986        public boolean areAllModuleDependenciesSatisfied(){
987                return ModuleDependencyUtil.checkIfModuleDependenciesSatisfied(this.getModuleDependencies());
988        }
989
990        /** Get a map of missing module dependencies. */
991        public Map<String, Version> getMissingDependencies() {
992                
993                List<String> dependencies = getModuleDependencies();
994                return ModuleDependencyUtil.getUnsatisfiedDependencies(dependencies);
995
996        }
997        
998        /**
999         * @return karVersions configuration property, adding it to config file
1000         * if it doesn't exist.
1001         */
1002        public static ConfigurationProperty getKARVersionsConfigProperty() {
1003                
1004                ConfigurationManager cman = ConfigurationManager.getInstance();
1005                ConfigurationProperty coreProperty = cman
1006                                .getProperty(KARFILE_CONFIG_PROP_MODULE);
1007                ConfigurationProperty KARVersionsProp = coreProperty
1008                        .getProperty(KAR_VERSIONS_PROPERTY_NAME);
1009                
1010                try {
1011                
1012                        // add karVersions configuration property (and all sub props) if
1013                        // necessary
1014                        if (KARVersionsProp == null) {
1015                                KARVersionsProp = new ConfigurationProperty(
1016                                                KARFILE_CONFIG_PROP_MODULE, KAR_VERSIONS_PROPERTY_NAME);
1017                                coreProperty.addProperty(KARVersionsProp);
1018
1019                                ConfigurationProperty KARCurrentVersionProp = new ConfigurationProperty(
1020                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1021                                                KARFile.KAR_CURRENT_VERSION_PROPERTY_NAME,
1022                                                KARFile.KAR_CURRENT_VERSION_DEFAULT);
1023
1024                                ConfigurationProperty KARVersion200Prop = new ConfigurationProperty(
1025                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1026                                                KARFile.KAR_VERSION_PROPERTY_NAME);
1027                                ConfigurationProperty KARVersion200VersionProp = new ConfigurationProperty(
1028                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1029                                                KARFile.KAR_VERSION_VERSION_PROPERTY_NAME,
1030                                                KARFile.KAR_VERSION_200_VERSION_DEFAULT);
1031                                ConfigurationProperty KARVersion200SchemaUrlProp = new ConfigurationProperty(
1032                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1033                                                KARFile.KAR_VERSION_SCHEMAURL_PROPERTY_NAME,
1034                                                KARFile.KAR_VERSION_200_SCHEMAURL_DEFAULT);
1035                                ConfigurationProperty KARVersion200NamespaceProp = new ConfigurationProperty(
1036                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1037                                                KARFile.KAR_VERSION_NAMESPACE_PROPERTY_NAME,
1038                                                KARFile.KAR_VERSION_200_NAMESPACE_DEFAULT);
1039                                ConfigurationProperty KARVersion200ResourceDirProp = new ConfigurationProperty(
1040                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1041                                                KARFile.KAR_VERSION_RESOURCEDIR_PROPERTY_NAME,
1042                                                KARFile.KAR_VERSION_200_RESOURCEDIR_DEFAULT);
1043                                ConfigurationProperty KARVersion200ResourceFileNameProp = new ConfigurationProperty(
1044                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1045                                                KARFile.KAR_VERSION_RESOURCE_FILENAME_PROPERTY_NAME,
1046                                                KARFile.KAR_VERSION_200_RESOURCE_FILENAME_DEFAULT);
1047                                
1048                                ConfigurationProperty KARVersion210Prop = new ConfigurationProperty(
1049                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1050                                                KARFile.KAR_VERSION_PROPERTY_NAME);
1051                                ConfigurationProperty KARVersion210VersionProp = new ConfigurationProperty(
1052                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1053                                                KARFile.KAR_VERSION_VERSION_PROPERTY_NAME,
1054                                                KARFile.KAR_VERSION_210_VERSION_DEFAULT);
1055                                ConfigurationProperty KARVersion210SchemaUrlProp = new ConfigurationProperty(
1056                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1057                                                KARFile.KAR_VERSION_SCHEMAURL_PROPERTY_NAME,
1058                                                KARFile.KAR_VERSION_210_SCHEMAURL_DEFAULT);
1059                                ConfigurationProperty KARVersion210NamespaceProp = new ConfigurationProperty(
1060                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1061                                                KARFile.KAR_VERSION_NAMESPACE_PROPERTY_NAME,
1062                                                KARFile.KAR_VERSION_210_NAMESPACE_DEFAULT);
1063                                ConfigurationProperty KARVersion210ResourceDirProp = new ConfigurationProperty(
1064                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1065                                                KARFile.KAR_VERSION_RESOURCEDIR_PROPERTY_NAME,
1066                                                KARFile.KAR_VERSION_210_RESOURCEDIR_DEFAULT);
1067                                ConfigurationProperty KARVersion210ResourceFileNameProp = new ConfigurationProperty(
1068                                                KARFile.KARFILE_CONFIG_PROP_MODULE,
1069                                                KARFile.KAR_VERSION_RESOURCE_FILENAME_PROPERTY_NAME,
1070                                                KARFile.KAR_VERSION_210_RESOURCE_FILENAME_DEFAULT);
1071
1072                                KARVersion200Prop.addProperty(KARVersion200VersionProp);
1073                                KARVersion200Prop.addProperty(KARVersion200SchemaUrlProp);
1074                                KARVersion200Prop.addProperty(KARVersion200NamespaceProp);
1075                                KARVersion200Prop.addProperty(KARVersion200ResourceDirProp);
1076                                KARVersion200Prop.addProperty(KARVersion200ResourceFileNameProp);
1077
1078                                KARVersion210Prop.addProperty(KARVersion210VersionProp);
1079                                KARVersion210Prop.addProperty(KARVersion210SchemaUrlProp);
1080                                KARVersion210Prop.addProperty(KARVersion210NamespaceProp);
1081                                KARVersion210Prop.addProperty(KARVersion210ResourceDirProp);
1082                                KARVersion210Prop.addProperty(KARVersion210ResourceFileNameProp);
1083
1084                                KARVersionsProp.addProperty(KARCurrentVersionProp);
1085                                KARVersionsProp.addProperty(KARVersion200Prop);
1086                                KARVersionsProp.addProperty(KARVersion210Prop);
1087                        }
1088
1089                } catch (NamespaceException e) {
1090                        // TODO Auto-generated catch block
1091                        e.printStackTrace();
1092                } catch (ConfigurationManagerException e) {
1093                        // TODO Auto-generated catch block
1094                        e.printStackTrace();
1095                }
1096                return KARVersionsProp;
1097                
1098        }
1099        
1100        /**
1101         * 
1102         * @return all KAR namespaces found in karVersions config property
1103         */
1104        public static List<String> getKARNamespaces() {
1105
1106                List<String> namespaces = new ArrayList<String>();
1107                ConfigurationProperty KARVersionsProp = KARFile.getKARVersionsConfigProperty();
1108
1109                Iterator<ConfigurationProperty> karVersionsItr = KARVersionsProp
1110                        .getProperties().iterator();
1111                
1112                while (karVersionsItr.hasNext()) {
1113                        ConfigurationProperty prop = karVersionsItr.next();
1114                        if (prop.getName().equals(KAR_VERSION_PROPERTY_NAME)) {
1115                                ConfigurationProperty namespaceConfigProp = prop
1116                                        .getProperty(KAR_VERSION_NAMESPACE_PROPERTY_NAME);
1117                                namespaces.add(namespaceConfigProp.getValue());
1118                        }
1119                }
1120
1121                return namespaces;
1122        }
1123
1124        /**
1125         * Return the local resource dir associated with given namespace, 
1126         * null if can't be found.
1127         * 
1128         * @param namespace
1129         * @return
1130         */
1131        public static String getResourceDir(String namespace) {
1132                ConfigurationProperty KARVersionsProp = KARFile
1133                                .getKARVersionsConfigProperty();
1134
1135                Iterator<ConfigurationProperty> karVersionsItr = KARVersionsProp
1136                                .getProperties().iterator();
1137
1138                while (karVersionsItr.hasNext()) {
1139                        ConfigurationProperty prop = karVersionsItr.next();
1140                        if (prop.getName().equals(KAR_VERSION_PROPERTY_NAME)) {
1141                                String namespaceFromConfig = prop
1142                                                .getProperty(KAR_VERSION_NAMESPACE_PROPERTY_NAME).getValue();
1143                                if (namespace.equals(namespaceFromConfig)){
1144                                        return prop.getProperty(KAR_VERSION_RESOURCEDIR_PROPERTY_NAME).getValue();
1145                                }
1146                        }
1147                }
1148                if (isDebugging)
1149                        log.error("Couldn't find resourceDir associated with namespace:"+namespace + " return null");
1150                return null;
1151        }
1152        
1153        /**
1154         * Return the local resource filename associated with given namespace, 
1155         * null if can't be found.
1156         * 
1157         * @param namespace
1158         * @return
1159         */
1160        public static String getResourceFileName(String namespace) {
1161                ConfigurationProperty KARVersionsProp = KARFile
1162                                .getKARVersionsConfigProperty();
1163
1164                Iterator<ConfigurationProperty> karVersionsItr = KARVersionsProp
1165                                .getProperties().iterator();
1166
1167                while (karVersionsItr.hasNext()) {
1168                        ConfigurationProperty prop = karVersionsItr.next();
1169                        if (prop.getName().equals(KAR_VERSION_PROPERTY_NAME)) {
1170                                String namespaceFromConfig = prop
1171                                                .getProperty(KAR_VERSION_NAMESPACE_PROPERTY_NAME).getValue();
1172                                if (namespace.equals(namespaceFromConfig)){
1173                                        return prop.getProperty(KAR_VERSION_RESOURCE_FILENAME_PROPERTY_NAME).getValue();
1174                                }
1175                        }
1176                }
1177                if (isDebugging)
1178                        log.error("Couldn't find resourceFileName associated with namespace:"+namespace + " return null");
1179                return null;
1180        }
1181
1182}