001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: tao $'
006 * '$Date: 2012-06-07 21:03:38 +0000 (Thu, 07 Jun 2012) $' 
007 * '$Revision: 29892 $'
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.provenance.kar;
031
032import java.io.File;
033import java.util.ArrayList;
034import java.util.Arrays;
035import java.util.Iterator;
036
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.ecoinformatics.ecogrid.client.IdentifierServiceClient;
040import org.kepler.kar.KARBuilder;
041import org.kepler.kar.KARFile;
042import org.kepler.objectmanager.ActorMetadata;
043import org.kepler.objectmanager.ObjectManager;
044import org.kepler.objectmanager.lsid.KeplerLSID;
045import org.kepler.objectmanager.lsid.LSIDGenerator;
046import org.kepler.objectmanager.repository.Repository;
047import org.kepler.objectmanager.repository.RepositoryManager;
048import org.kepler.util.WorkflowRun;
049
050import ptolemy.actor.TypedCompositeActor;
051import ptolemy.kernel.util.NamedObj;
052import ptolemy.kernel.util.StringAttribute;
053
054/**
055 * This class allows for uploading KARs to the repository without user
056 * interaction
057 */
058
059/**
060 *  '$Author: tao $'
061 *  '$Date: 2012-06-07 21:03:38 +0000 (Thu, 07 Jun 2012) $'
062 *  '$Revision: 29892 $'
063 *
064 *  For Details:
065 *  http://www.kepler-project.org
066 *
067 *  Copyright (c) 2009-2010 The Regents of the
068 *  University of California. All rights reserved. Permission is hereby granted,
069 *  without written agreement and without license or royalty fees, to use, copy,
070 *  modify, and distribute this software and its documentation for any purpose,
071 *  provided that the above copyright notice and the following two paragraphs
072 *  appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF
073 *  CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
074 *  OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
075 *  DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
076 *  POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY
077 *  DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
078 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
079 *  SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
080 *  CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
081 *  ENHANCEMENTS, OR MODIFICATIONS.
082 */
083
084public class UploadKarToRepositoryNoGUI 
085{
086
087        public static final Log log = LogFactory.getLog(UploadKarToRepositoryNoGUI.class);
088        
089        /**
090         * Constructor
091         */
092        public UploadKarToRepositoryNoGUI() 
093        {
094        }
095
096        /**
097         * uploads KAR to the repository
098         * 
099         * @return true if the upload was successful
100         */
101        public boolean uploadKAR(File karFile, String sessionId, boolean isPublic) 
102        {
103                if (!karFile.exists()) {
104                        return false;
105                }
106                // get the runs and unmodified workflow MoMLs out of objectManager
107                try {
108                        KARFile karf = new KARFile(karFile);
109                        KeplerLSID karLSID = karf.getLSID();
110                        
111                        ArrayList<KeplerLSID> workflowRunLSIDList = new ArrayList<KeplerLSID>();
112                        ArrayList<KeplerLSID> typedCompositeActorLSIDList = new ArrayList<KeplerLSID>();
113                        ArrayList<KeplerLSID> metadataLSIDsToUpload = new ArrayList<KeplerLSID>();
114                        ArrayList<Object> metadataObjectsToUpload = new ArrayList<Object>();
115
116                        workflowRunLSIDList.addAll(
117                                        Arrays.asList(karf.getContentOfType(WorkflowRun.class.getName())));
118                        typedCompositeActorLSIDList.addAll(
119                                        Arrays.asList(karf.getContentOfType(TypedCompositeActor.class.getName())));
120
121
122                        // check that workflowRuns karLSIDs equal KARFile LSID
123                        Iterator<KeplerLSID> entryLsidItr = workflowRunLSIDList.iterator();
124                        KeplerLSID entryLsid;
125
126                        while (entryLsidItr.hasNext()) 
127                        {
128                                entryLsid = entryLsidItr.next();
129
130                                // get the run from the object manager
131                                WorkflowRun run = (WorkflowRun) ObjectManager.getInstance().getHighestObjectRevision(entryLsid);
132                                if (run == null) 
133                                {
134                                        log.error("workflow run from object manager is null");
135                                        return false;
136                                }
137
138                                log.debug("karentry: "
139                                                                + run.getName()
140                                                                + " will send the entry to the repo as metadata");
141                                metadataLSIDsToUpload.add(entryLsid);
142                                metadataObjectsToUpload.add(run);
143                                
144                        }
145
146                        // 06.17.09 No longer check the workflows -- we've decided to change
147                        // to: inside-kar workflows will not have karLSID.
148                        // Kars will be self contained. All workflows found in the kar will
149                        // be used to create ActorMetadata
150                        // docs that point to this kar with a karLSID.
151                        entryLsidItr = typedCompositeActorLSIDList.iterator();
152                        while (entryLsidItr.hasNext()) 
153                        {
154                                entryLsid = entryLsidItr.next();
155                                NamedObj no = ObjectManager.getInstance().getHighestObjectRevision(entryLsid);
156                                if (no != null) 
157                                {
158                                        // create an AM object from the entity
159                                        ActorMetadata am = new ActorMetadata(no);
160                                        // log.debug("am.toString()\n"+am.toString());
161                                        StringAttribute karLsidAttr = new StringAttribute();
162                                        // put kar lsid in metadata
163                                        karLsidAttr.setName(KARBuilder.KAR_LSID_ATTRIBUTE_NAME); 
164                                        karLsidAttr.setExpression(karLSID.toString());
165                                        am.addAttribute(karLsidAttr);
166                                        addDocumentation(am);
167                                        metadataLSIDsToUpload.add(entryLsid);
168                                        metadataObjectsToUpload.add(am);
169                                } 
170                                else 
171                                {
172                                        return false;
173                                }
174                        }
175
176                        RepositoryManager manager;
177                        try 
178                        {
179                                manager = RepositoryManager.getInstance();
180                        } catch (Exception e) 
181                        {
182                                log.error("Could not get repository manager: " + e.getMessage());
183                                e.printStackTrace();
184                                return false;
185                        }
186
187                        // get the save repository and set the lsid server url
188                        Repository repository = manager.getSaveRepository();
189                        log.debug("Using repository:" + repository);
190                        
191                        String LSIDServer = repository.getLSIDServerURL();
192                        IdentifierServiceClient lsidClient = new IdentifierServiceClient(LSIDServer);
193
194                        // check if lsid isRegistered
195                        // FIXME hardcode for now if the kar lsid is already registered, we
196                        // just give up (return false)
197                        // currently we're not getting kars from cache or objectmanager, so
198                        // each will have a new lsid and
199                        // will therefore never be registered.
200                        boolean isKarRegistered = lsidClient.isRegistered(karLSID.toString());
201                        log.debug("is karLSID registered? " + isKarRegistered);
202                        if (isKarRegistered) 
203                        {
204                                // it's already used. get another one.
205                                // ask the user if they really want to upload since the
206                                // object already exists
207
208                                String lsidStr = 
209                                        "urn:lsid:" + karLSID.getAuthority() + ":"
210                                        + karLSID.getNamespace() + ":1:1";
211                                log.debug("getting next object for lsid " + lsidStr);
212                                String newLsidString = lsidClient.getNextObject(lsidStr);
213                                KeplerLSID newLSID = new KeplerLSID(newLsidString);
214                                log.debug("lsid is already registered, will use this new one:" + newLSID);
215
216                                return false;
217                        } 
218                        else 
219                        {
220                                log.debug("lsid not registered in repository, didn't need to get a new one.");
221                        }
222
223                        // put the kar file
224                        repository.put(karFile, karLSID, sessionId);
225                        log.debug("uploaded kar file with lsid " + karLSID);
226                        
227                        // create an access file so we can make this kar public on the ecogrid
228                        // TODO use setAccess instead of generating access files.
229                        KeplerLSID karAccessLSID = LSIDGenerator.getInstance().getNewLSID();
230                        repository.put(
231                                        buildAccessDocument(karLSID, isPublic),
232                                        karAccessLSID, sessionId);
233                        log.debug("uploaded access doc for kar file, lsid " + karAccessLSID);
234
235                        // put any metadata docs that have karLSID == this kar's lsid.
236                        Iterator<KeplerLSID> metadataLsidLitr = metadataLSIDsToUpload.iterator();
237                        Iterator<Object> metadataObjLitr = metadataObjectsToUpload.iterator();
238
239                        log.debug("uploading metadata objects, count = " + metadataLSIDsToUpload.size());
240                        
241                        while (metadataLsidLitr.hasNext()) 
242                        {
243                                KeplerLSID metadataLsid = metadataLsidLitr.next();
244                                Object metadataNamedObj = metadataObjLitr.next();
245                                if (metadataNamedObj != null) 
246                                {
247                                        
248                                        // check if we have tried uploading this before
249                                        boolean isMetadataRegistered = lsidClient.isRegistered(metadataLsid.toString());
250                                        if (!isMetadataRegistered) 
251                                        {
252                                                // upload the metadata document
253                                                repository.put(metadataNamedObj, metadataLsid, sessionId);
254                                                log.debug("uploaded metadata object " + metadataNamedObj.getClass().getName() + ", with lsid " + metadataLsid);
255        
256                                                // do the access document for it
257                                                KeplerLSID accessLSID = 
258                                                        LSIDGenerator.getInstance().getNewLSID();
259                                                repository.put(
260                                                                buildAccessDocument(metadataLsid, isPublic),
261                                                                accessLSID, sessionId);
262                                                log.debug("uploaded access (for metadata object) with lsid " + accessLSID);
263                                        }
264                                        else 
265                                        {
266                                                log.warn("metadata already exists for LSID: " + metadataLsid);
267                                        }
268                                }
269                        }
270
271                        return true;
272                } 
273                catch (Exception e) 
274                {
275                        log.error("there was a problem uploading the data to repository");
276                        e.printStackTrace();
277                        return false;
278                }
279        }
280
281        /**
282         * uploads a document as the kepler user with public read permissions
283         */
284        public static String buildAccessDocument(KeplerLSID lsid) 
285        {
286                return buildAccessDocument(lsid, null, true);
287        }
288
289        public static String buildAccessDocument(KeplerLSID lsid, boolean publicDoc) 
290        {
291                return buildAccessDocument(lsid, null, publicDoc);
292        }
293
294        /**
295         * build an access document that gives 'public' read access to the inserted
296         * documents.
297         */
298        public static String buildAccessDocument(KeplerLSID lsid, String owner,
299                        boolean publicDoc) 
300        {
301                StringBuffer sb = new StringBuffer();
302                sb.append("<?xml version=\"1.0\"?>\n");
303                sb
304                                .append("<eml:eml packageId=\"\" system=\"knb\" "
305                                                + "xmlns:eml=\"eml://ecoinformatics.org/eml-2.0.1\" "
306                                                + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
307                                                + "xsi:schemaLocation=\"eml://ecoinformatics.org/eml-2.0.1 eml.xsd\">\n");
308                sb.append("<dataset>\n");
309                sb.append("<title>access for " + lsid.toString() + "</title>\n");
310                sb.append("<creator id=\"1\">\n");
311                sb.append("  <individualName><surName>kepler</surName>\n");
312                sb.append("  </individualName>\n");
313                sb.append("</creator>\n");
314
315                sb.append("<contact><references>1</references>\n");
316                sb.append("</contact>\n");
317
318                if (publicDoc) 
319                {
320                        sb.append("<access authSystem=\"knb\" order=\"allowFirst\">\n");
321                        sb.append("  <allow>\n");
322                        sb.append("    <principal>public</principal>\n");
323                        sb.append("    <permission>read</permission>\n");
324                        sb.append("  </allow>\n");
325                        sb.append("</access>\n");
326                }
327
328                if (owner != null) 
329                {
330                        sb.append("<access authSystem=\"knb\" order=\"allowFirst\">\n");
331                        sb.append("  <allow>\n");
332                        sb.append("    <principal>" + owner + "</principal>\n");
333                        sb.append("    <permission>read</permission>\n");
334                        sb.append("    <permission>write</permission>\n");
335                        sb.append("  </allow>\n");
336                        sb.append("</access>\n");
337                }
338
339                sb.append("<dataTable id=\"x\">\n");
340
341                sb.append("<entityName>asdf</entityName>\n");
342
343                sb.append("<physical>\n");
344                sb.append("  <objectName>tmp</objectName>\n");
345                sb.append("  <dataFormat> \n");
346                sb.append("  <externallyDefinedFormat>\n");
347                sb.append("    <formatName>application/vnd.ms-excel</formatName>\n");
348                sb.append("  </externallyDefinedFormat>\n");
349                sb.append("  </dataFormat>\n");
350
351                sb.append("  <distribution>\n");
352                sb.append("    <online>\n");
353                sb.append("      <url>ecogrid://knb/" + lsid.getNamespace() + "."
354                                + lsid.getObject() + "." + lsid.getRevision() + "</url>\n");
355                sb.append("    </online>\n");
356                sb.append("   </distribution>\n");
357                sb.append("</physical>\n");
358
359                sb.append("<attributeList>\n");
360                sb.append("  <attribute id=\"2\">\n");
361                sb.append("    <attributeName>0</attributeName>\n");
362                sb.append("    <attributeDefinition>0</attributeDefinition>\n");
363                sb.append("    <measurementScale>\n");
364                sb.append("      <interval>\n");
365                sb.append("        <unit>\n");
366                sb
367                                .append("          <standardUnit>metersPerSecondSquared</standardUnit>\n");
368                sb.append("        </unit>\n");
369                sb.append("        <precision>.2</precision>\n");
370                sb.append("        <numericDomain>\n");
371                sb.append("          <numberType>natural</numberType>\n");
372                sb.append("        </numericDomain>\n");
373                sb.append("      </interval>\n");
374                sb.append("    </measurementScale>\n");
375                sb.append("  </attribute>\n");
376                sb.append("</attributeList>\n");
377
378                sb.append("</dataTable>\n");
379                sb.append("</dataset>\n");
380                sb.append("</eml:eml>\n");
381
382                return sb.toString();
383        }
384
385        /**
386         * adds the generated documentation (if it exists) to the actor metadata
387         */
388        public static void addDocumentation(ActorMetadata am) throws Exception 
389        {
390                /*
391                 * //look for the documentation KeplerDocApplicationSpecializer kdas =
392                 * new KeplerDocApplicationSpecializer(); String actorName =
393                 * am.getName(); URL fileUrl = kdas.docClassNameToURL("",
394                 * am.getClassName(), true, true, false, false);
395                 * 
396                 * if(fileUrl == null || fileUrl.toString().trim().equals("")) { //if
397                 * not found, exit System.out.println("No documentation found for " +
398                 * am.getName()); return; } //get the documentation String filename =
399                 * fileUrl.toString().substring(5, fileUrl.toString().length()); File f
400                 * = new File(filename);
401                 * 
402                 * //parse the xml DocumentBuilder parser = ConfigXML.createDomParser();
403                 * InputSource in; in = new InputSource(new FileReader(filename));
404                 * Document doc = parser.parse(in); Node root =
405                 * doc.getDocumentElement(); //get description String descVal = "";
406                 * String authVal = ""; String verVal = ""; String uldVal = "";
407                 * 
408                 * Node descNode = XPathAPI.selectSingleNode(doc, "/doc/description");
409                 * if(descNode != null) descVal =
410                 * descNode.getFirstChild().getNodeValue(); //get author Node authNode =
411                 * XPathAPI.selectSingleNode(doc, "/doc/author"); if(authNode != null)
412                 * authVal = authNode.getFirstChild().getNodeValue(); //get version Node
413                 * verNode = XPathAPI.selectSingleNode(doc, "/doc/version"); if(verNode
414                 * != null) verVal = verNode.getFirstChild().getNodeValue(); //get
415                 * UserLevelDocumentation Node uldNode = XPathAPI.selectSingleNode(doc,
416                 * "/doc/UserLevelDocumentation"); if(uldNode != null) uldVal =
417                 * uldNode.getFirstChild().getNodeValue();
418                 * 
419                 * //get ports Hashtable portHash = new Hashtable(); NodeList portList =
420                 * XPathAPI.selectNodeList(doc, "/doc/port"); for(int i=0;
421                 * i<portList.getLength(); i++) { String name = ""; String val = "";
422                 * Node portNode = portList.item(i); Node nameNode =
423                 * portNode.getAttributes().getNamedItem("name"); if(nameNode != null) {
424                 * name = nameNode.getNodeValue(); } NodeList children =
425                 * portNode.getChildNodes(); for(int j=0; j<children.getLength(); j++) {
426                 * Node n = children.item(j); val = n.getNodeValue(); } if(val == null)
427                 * val = "";
428                 * 
429                 * if(name != null) portHash.put(name, val); }
430                 * 
431                 * //get properties Hashtable propHash = new Hashtable(); NodeList
432                 * propList = XPathAPI.selectNodeList(doc, "/doc/property"); for(int
433                 * i=0; i<propList.getLength(); i++) { String name = ""; String val =
434                 * ""; Node propNode = propList.item(i); Node nameNode =
435                 * propNode.getAttributes().getNamedItem("name"); if(nameNode != null) {
436                 * name = nameNode.getNodeValue(); } NodeList children =
437                 * propNode.getChildNodes(); for(int j=0; j<children.getLength(); j++) {
438                 * Node n = children.item(j); val = n.getNodeValue(); } if(val == null)
439                 * val = ""; if(name != null) propHash.put(name, val); }
440                 * 
441                 * //now we have all the data, we need to add it to the actor metadata
442                 * KeplerDocumentationAttribute da = new KeplerDocumentationAttribute();
443                 * da.setName("documentation"); da.setDescription(descVal);
444                 * da.setAuthor(authVal); da.setVersion(verVal);
445                 * da.setUserLevelDocumentation(uldVal); da.setPortHash(portHash);
446                 * da.setPropertyHash(propHash); am.addAttribute(da);
447                 */
448        }
449
450}