001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: tao $'
006 * '$Date: 2014-04-21 23:34:35 +0000 (Mon, 21 Apr 2014) $' 
007 * '$Revision: 32688 $'
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.repository;
031
032import java.io.ByteArrayInputStream;
033import java.io.ByteArrayOutputStream;
034import java.io.File;
035import java.io.InputStream;
036import java.io.Reader;
037import java.io.StringReader;
038import java.net.URL;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Vector;
042
043import org.apache.commons.logging.Log;
044import org.apache.commons.logging.LogFactory;
045import org.ecoinformatics.ecogrid.EcogridObjType;
046import org.ecoinformatics.ecogrid.authenticatedqueryservice.AuthenticatedQueryServiceClient;
047import org.ecoinformatics.ecogrid.authenticatedqueryservice.AuthenticatedQueryServiceGetToStreamClient;
048import org.ecoinformatics.ecogrid.client.AuthenticationServiceClient;
049import org.ecoinformatics.ecogrid.client.IdentifierServiceClient;
050import org.ecoinformatics.ecogrid.client.PutServiceClient;
051import org.ecoinformatics.ecogrid.client.RegistryServiceClient;
052import org.ecoinformatics.ecogrid.queryservice.QueryServiceClient;
053import org.ecoinformatics.ecogrid.queryservice.query.QueryType;
054import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetType;
055import org.ecoinformatics.ecogrid.queryservice.resultset.ResultsetTypeRecord;
056import org.ecoinformatics.ecogrid.queryservice.stub.QueryServiceStub;
057import org.ecoinformatics.ecogrid.registry.stub.RegistryEntryType;
058import org.kepler.authentication.AuthenticationException;
059import org.kepler.authentication.AuthenticationManager;
060import org.kepler.authentication.ProxyEntity;
061import org.kepler.gui.CanvasDropTargetListener;
062import org.kepler.kar.KARFile;
063import org.kepler.kar.karxml.KarXml;
064import org.kepler.objectmanager.lsid.KeplerLSID;
065
066import util.StaticUtil;
067
068/**
069 * This class represents an ecogrid repository
070 * 
071 * @author Chad Berkley
072 */
073public class EcogridRepository extends Repository {
074        private static final Log log = LogFactory.getLog(EcogridRepository.class
075                        .getName());
076        private static final boolean isDebugging = log.isDebugEnabled();
077        
078        private static final String WORKFLOWRUNTYPE = "org.kepler.util.WorkflowRun";
079
080        private String ECOGRIDPUTSERVER;
081        private String ECOGRIDLSIDSERVER;
082        private String ECOGRIDQUERYSERVER;
083        private String ECOGRIDREGAUTHSERVER;
084        private String ECOGRIDAUTHORIZATIONSERVER;
085        private String ECOGRIDAUTHENTICATEDQUERYSERVICE;
086        private String regsessionid;
087
088        public EcogridRepository(String name, String repository, String putPath,
089                        String authDomain, String lsidPath, String queryPath, String authenticatedQueryPath, 
090                        String authorizationPath, String registry, String registryauth,
091                        String authProtocol, String lsidAuthority) {
092                super(name, repository, putPath, authDomain, lsidPath, queryPath, authenticatedQueryPath,
093                                authorizationPath, registry, registryauth, authProtocol, lsidAuthority);
094                ECOGRIDPUTSERVER = authProtocol +"://"+ repository + putPath;
095                ECOGRIDLSIDSERVER = authProtocol +"://" + repository + lsidPath;
096                ECOGRIDQUERYSERVER = authProtocol +"://"+ repository + queryPath;
097                ECOGRIDAUTHORIZATIONSERVER = authProtocol +"://"+ repository + authorizationPath;
098                ECOGRIDREGAUTHSERVER = registryauth;
099                ECOGRIDAUTHENTICATEDQUERYSERVICE = authProtocol +"://"+ repository + authenticatedQueryPath;
100
101                if (isDebugging) {
102                        log.debug("repository initialized with put server: "
103                                        + ECOGRIDPUTSERVER);
104                        log.debug("repository initialized with lsid server: "
105                                        + ECOGRIDLSIDSERVER);
106                        log.debug("repository initialized with query server: "
107                                        + ECOGRIDQUERYSERVER);
108                        log.debug("repository initialized with registry: " + registry);
109                }
110
111                CanvasDropTargetListener cdtListener = CanvasDropTargetListener
112                                .getInstance();
113                cdtListener
114                                .registerListener(new EcogridRepositoryKARDownloadListener());
115        }
116
117        /**
118         * return the url for the lsid service associated with this repository
119         */
120        public String getLSIDServerURL() {
121                return ECOGRIDLSIDSERVER;
122        }
123        
124        /**
125         * Get the url for the authorization service associated with this repository
126         * @return
127         */
128        public String getAuthorizationServerURL(){
129          return ECOGRIDAUTHORIZATIONSERVER;
130        }
131
132        /**
133         * Search the repository and return an iterator of EcogridRepositoryResults.
134         * 
135         * @see EcogridRepositoryResults
136         * @param queryString
137         *            a string to search for
138         * @param authenticate
139         *                      boolean
140         * @return null if there are no results for the query. An iterator of
141         *         EcogridRepositoryResults if there are results.
142         * @throws AuthenticationException 
143         */
144        public Iterator<EcogridRepositoryResults> search(String queryString, boolean authenticate)
145                        throws RepositoryException, AuthenticationException {
146                if (isDebugging) {
147                        log.debug("search(\"" + queryString + "\")");
148                }
149                
150                Vector<EcogridRepositoryResults> resultsVector = new Vector<EcogridRepositoryResults>();
151                
152                        //System.out.println("EcogridRepository search(queryString,"+authenticate+") querying with queryString:" + 
153                        //              queryString);
154                        ResultsetType rst = arbitrarySearch(buildQueryDoc(queryString), authenticate);
155                        if (rst == null) { // check to see if the resultsettype is null
156                                return null;
157                        }
158
159                        ResultsetTypeRecord[] records = rst.getRecord();
160                        if (records == null || records.length == 0) { 
161                                // check to see if there are records
162                                return null;
163                        }
164
165                        if (isDebugging) {
166                                log.debug("There are " + records.length + " records");
167                        }
168                        
169                        // create the EcogridRepositoryResult object and put it in the
170                        // vector
171                        for (int i = 0; i < records.length; i++) {
172                                try { // catch this here so one result can't hose the whole
173                                        // resultset                                    
174                                        List<EcogridRepositoryResults> results = 
175                                                EcogridRepositoryResults.parseKarXml(
176                                                                records[i].getIdentifier(), name, i, authenticate);
177                                        
178                                        resultsVector.addAll(results);
179                                } catch (Exception e) {
180                                        System.out.println("could not load result: "
181                                                        + records[i].toString() + "  error: "
182                                                        + e.getMessage());
183                                }
184                                // ResultsetTypeRecord currentRecord = records[i];
185                        }
186
187                        return resultsVector.iterator();
188        }
189        
190        /**
191         * Search the repository and return an iterator of EcogridRepositoryResults.
192         * 
193         * @param queryDocument
194         *            - the query document to give the QueryServiceClient
195         * @return
196         * @throws RepositoryException
197         * @throws AuthenticationException
198         */
199        public Iterator<EcogridRepositoryResults> advancedSearch(
200                        Reader queryDocument, boolean authenticate)
201                        throws RepositoryException, AuthenticationException {
202
203                Vector<EcogridRepositoryResults> resultsVector = new Vector<EcogridRepositoryResults>();
204
205                ResultsetType rst = arbitrarySearch(queryDocument, authenticate);
206                if (rst == null) { // check to see if the resultsettype is null
207                        return null;
208                }
209
210                ResultsetTypeRecord[] records = rst.getRecord();
211                if (records == null || records.length == 0) { // check to see if
212                        // there are records
213                        return null;
214                }
215
216                if (isDebugging) {
217                        log.debug("There are " + records.length + " records");
218                }
219
220                // create the EcogridRepositoryResult object and put it in the
221                // vector
222                for (int i = 0; i < records.length; i++) {
223                        try { // catch this here so one result can't hose the whole
224                                        // resultset
225                                List<EcogridRepositoryResults> results = EcogridRepositoryResults
226                                                .parseKarXml(records[i].getIdentifier(), name, i,
227                                                                authenticate);
228                                resultsVector.addAll(results);
229                        } catch (Exception e) {
230                                System.out.println("could not load result: "
231                                                + records[i].toString() + "  error: " + e.getMessage());
232                        }
233                        // ResultsetTypeRecord currentRecord = records[i];
234                }
235
236                return resultsVector.iterator();
237        }
238        
239        /**
240         * Search the repository using queryDocument.
241         * @param queryDocument
242         * @return ResultsetType from QueryServiceClient
243         * @throws RepositoryException
244         */
245        private ResultsetType arbitrarySearch(Reader queryDocument)
246                        throws RepositoryException {
247
248                try {
249                        QueryServiceClient qclient = new QueryServiceClient(
250                                        ECOGRIDQUERYSERVER);
251                        ResultsetType rst = qclient.query(queryDocument);
252                        return rst;
253                        
254                } catch (Exception e) {
255                        e.printStackTrace();
256                        throw new RepositoryException(
257                                        "Error searching ecogrid repository: " + e.getMessage());
258                }
259        }
260        
261
262        /**
263         * Search the repository using queryDocument.
264         * 
265         * @param queryDocument
266         * @param authenticate
267         * @return ResultsetType, or null
268         * @throws RepositoryException
269         * @throws AuthenticationException
270         */
271        public ResultsetType arbitrarySearch(Reader queryDocument,
272                        boolean authenticate) throws RepositoryException,
273                        AuthenticationException {
274
275                if (!authenticate) {
276                        return arbitrarySearch(queryDocument);
277                }
278
279                String sessionId = authenticate();
280                if (sessionId == null || sessionId.isEmpty()) {
281                        return null;
282                }
283
284                QueryType queryType = AuthenticatedQueryServiceClient
285                                .reader2QueryType(queryDocument);
286                try {
287                        AuthenticatedQueryServiceClient authQueryClient = new AuthenticatedQueryServiceClient(
288                                        ECOGRIDAUTHENTICATEDQUERYSERVICE);
289                        ResultsetType rst = authQueryClient.query(queryType, sessionId);
290
291                        return rst;
292
293                } catch (Exception e) {
294                        e.printStackTrace();
295                        throw new RepositoryException(
296                                        "Error searching ecogrid repository: " + e.getMessage());
297                }
298        }
299
300        /**
301         *
302         * @return sessionId, or null
303         * @throws AuthenticationException 
304         */
305        private String authenticate() throws AuthenticationException {
306                AuthenticationManager authManager = AuthenticationManager.getManager();
307                // first peek to see if already authenticated
308                ProxyEntity proxy;
309                
310                proxy = authManager.peekProxy(authDomain);
311
312                // authenticate if necessary
313                if (proxy == null) {
314                        proxy = authManager.getProxy(authDomain);
315                }
316                return proxy.getCredential();
317        }
318        
319        /**
320         * return an object using a ecogrid docid identifier
321         * @throws AuthenticationException 
322         */
323        public InputStream get(String docid, boolean authenticate) throws RepositoryException, AuthenticationException {
324                
325                        if (!authenticate){
326                                return get(docid);
327                        }
328                        
329                        String sessionId = authenticate();
330                        if (sessionId == null || sessionId.isEmpty()){
331                                return null;
332                        }
333                        try {
334                        URL url = new URL(ECOGRIDAUTHENTICATEDQUERYSERVICE);
335                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
336                        AuthenticatedQueryServiceGetToStreamClient aqsgtsc = 
337                                new AuthenticatedQueryServiceGetToStreamClient(url);
338                        aqsgtsc.get(docid, sessionId, outputStream);
339                        outputStream.close();
340
341                        return new ByteArrayInputStream(outputStream.toByteArray());
342                        
343                } catch (Exception e) {
344                        throw new RepositoryException("Error getting docid " + docid + ": "
345                                        + e.getMessage());
346                }
347        }
348        
349        /**
350         * return an object using a ecogrid docid identifier
351         */
352        private InputStream get(String docid) throws RepositoryException {
353                try {
354                        QueryServiceStub client = new QueryServiceStub(new URL(
355                                        ECOGRIDQUERYSERVER), null);
356                        byte[] b = client.get(docid);
357                        return new ByteArrayInputStream(b);
358                } catch (Exception e) {
359                        throw new RepositoryException("Error getting docid " + docid + ": "
360                                        + e.getMessage());
361                }
362        }
363
364        /**
365         * Return karXml for a docid from a repositoryName. Move this method
366         * to another class if you can think of a better home.
367         * 
368         * @param docid
369         * @param repositoryName
370         * @return
371         * @throws RepositoryException
372         */
373        public static KarXml getKarXml(String docid, String repositoryName, boolean authenticate) 
374                throws RepositoryException {
375
376                try {
377                        RepositoryManager rm = RepositoryManager.getInstance();
378                        Repository rep = rm.getRepository(repositoryName);
379                        if (!(rep instanceof EcogridRepository)) {
380                                log.error("EcogridRepository getKarXml is trying to search "
381                                                + "a non-Ecogrid Repository");
382                                return null;
383                        }
384                        EcogridRepository erep = (EcogridRepository) rep;
385                        InputStream is = erep.get(docid, authenticate);
386
387                        return KarXml.of(is);
388                } catch (Exception ex) {
389                        ex.printStackTrace();
390                }
391                return null;
392        }
393        
394        /**
395         * return the object from the repository that has the given lsid
396         * @throws AuthenticationException 
397         */
398        public InputStream get(KeplerLSID lsid, boolean authenticate) throws RepositoryException, AuthenticationException {
399                /*
400                 * NOTE: this is a bad way to do this. this should use the lsid
401                 * authority to pull the object from the grid. For the sake of getting
402                 * something working, i'm leaving this in here for now. -CB
403                 */
404                String docid = lsid.getNamespace() + "." + lsid.getObject() + "."
405                                + lsid.getRevision();
406                return get(docid, authenticate);
407        }
408
409        /**
410         * put a file with a predetermined sessionid
411         */
412        public void put(Object o, KeplerLSID lsid, String sessionId)
413                        throws RepositoryException {
414                try {
415                        String docid = lsid.getNamespace() + "." + lsid.getObject() + "."
416                                        + lsid.getRevision();
417                        if (o instanceof File) {
418                                uploadDataFile(((File) o), docid, sessionId);
419                        } else {
420                                uploadMetadata(o.toString(), docid, sessionId);
421                        }
422                } catch (Exception e) {
423                        throw new RepositoryException(e.getMessage());
424                }
425        }
426
427        /**
428         * returns the next object for the given lsid
429         * 
430         * @param lsid
431         *            the lsid to get the next object for
432         */
433        public String getNextObject(KeplerLSID lsid) throws RepositoryException {
434                try {
435                        IdentifierServiceClient lsidClient = new IdentifierServiceClient(
436                                        ECOGRIDLSIDSERVER);
437                        return lsidClient.getNextObject(lsid.toString());
438                } catch (Exception e) {
439                        throw new RepositoryException("Error getting next object: "
440                                        + e.getMessage());
441                }
442        }
443
444        /**
445         * returns the next revision for the given lsid
446         * 
447         * @param lsid
448         *            the lsid to get the next revision for
449         */
450        public String getNextRevision(KeplerLSID lsid) throws RepositoryException {
451                try {
452                        IdentifierServiceClient lsidClient = new IdentifierServiceClient(
453                                        ECOGRIDLSIDSERVER);
454                        return lsidClient.getNextRevision(lsid.toString());
455                } catch (Exception e) {
456                        throw new RepositoryException("Error getting next revision: "
457                                        + e.getMessage());
458                }
459        }
460
461        // ////////////////////////////////////////////////////
462        // ///// ecogrid registry methods /////////
463
464        /**
465         * add an ecogrid registry entry.
466         * 
467         * @param registryEntry
468         *            an xml file that conforms to
469         *            seek/project/ecogrid/src/xsd_reg/RegistryEntryType.xsd
470         * @param sessionid
471         *            the session id to use to authenticate
472         */
473        public KeplerLSID addRegistryEntry(RegistryEntryType registryEntry, String sessionId)
474                        throws RepositoryException {
475                try {
476                        RegistryServiceClient client = new RegistryServiceClient(registry);
477                        // String docid = client.add(sessionId, new
478                        // StringReader(registryEntry));
479                        String docid = client.add(sessionId, registryEntry);
480                        KeplerLSID lsid = new KeplerLSID(docid, "kepler-project.org");
481                        return lsid;
482                } catch (Exception e) {
483                        e.printStackTrace();
484                        throw new RepositoryException("Error adding registry entry: "
485                                        + e.getMessage());
486                }
487        }
488
489        /**
490         * update an ecogrid registry entry.
491         * 
492         * @param registryEntry
493         *            an xml file that conforms to
494         *            seek/project/ecogrid/src/xsd_reg/RegistryEntryType.xsd
495         * @param lsid
496         *            the id of the entry
497         * @param sessionid
498         *            the session id to use to authenticate
499         */
500        public void updateRegistryEntry(String registryEntry, KeplerLSID lsid)
501                        throws RepositoryException {
502                throw new RepositoryException("Not yet implemented.");
503        }
504
505        /**
506         * remove an ecogrid registry entry.
507         * 
508         * @param lsid
509         *            the id of the entry
510         * @param sessionid
511         *            the session id to use to authenticate
512         */
513        public void removeRegistryEntry(KeplerLSID lsid, String sessionId) throws RepositoryException {
514                try {
515                        String newdocid = lsid.getNamespace() + "." + lsid.getObject()
516                                        + "." + lsid.getRevision();
517                        RegistryServiceClient client = new RegistryServiceClient(registry);
518                        String docid = client.remove(sessionId, newdocid);
519                } catch (Exception e) {
520                        e.printStackTrace();
521                        throw new RepositoryException(
522                                        "Error removing ecogrid registry entry.");
523                }
524        }
525
526        /**
527         * login to the registry
528         */
529        public String loginRegEcoGrid(String userName, String password)
530                        throws Exception {
531                AuthenticationServiceClient client = new AuthenticationServiceClient(
532                                ECOGRIDREGAUTHSERVER);
533                regsessionid = client.login_action(userName, password);
534                return regsessionid;
535        }
536
537        /*
538         * Method to upload data
539         */
540        private void uploadDataFile(File localFile, String docid,
541                        String sessionId) throws Exception {
542                String localFilePath = localFile.getAbsolutePath();
543                String localFileName = localFile.getName();
544                
545                int type = EcogridObjType.DATA;
546                PutServiceClient client = new PutServiceClient(ECOGRIDPUTSERVER);
547                byte[] data = StaticUtil.getBytesArrayFromFile(localFilePath);
548                client.put(data, docid, localFileName, type, sessionId);
549        }
550
551        /*
552         * Method to upload metadata
553         */
554        private void uploadMetadata(String metadataContent, String docid,
555                        String sessionId) throws Exception {
556                int type = EcogridObjType.METADATA;
557                byte[] content = metadataContent.getBytes();
558                PutServiceClient client = new PutServiceClient(ECOGRIDPUTSERVER);
559                client.put(content, docid, type, sessionId);
560        }
561
562        /**
563         * builds an ecogrid query document with the user's query string in it.
564         */
565        private Reader buildQueryDoc(String queryString) {
566                
567                // we query all KAR namespaces
568                Iterator<String> namespaceItr = KARFile.getKARNamespaces().iterator();
569                
570                StringBuffer sb = new StringBuffer();
571                sb
572                                .append("<egq:query queryId=\"test.1.1\" system=\"http://knb.ecoinformatics.org\" ");
573                sb.append("xmlns:egq=\"http://ecoinformatics.org/query-1.0.1\" ");
574                sb.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
575                sb
576                                .append("xsi:schemaLocation=\"http://ecoinformatics.org/query-1.0.1 ../../src/xsd/query.xsd\">\n");
577                //sb.append("<namespace prefix=\"kepler\">kar</namespace>\n");
578                
579                while (namespaceItr.hasNext()){
580                        sb.append("<namespace prefix=\"kepler\">");
581                        sb.append(namespaceItr.next());
582                        sb.append("</namespace>\n");
583                }
584                sb.append("<returnField>/entity/@name</returnField>\n");
585                // sb.append("<returnField>/entity/property/@name</returnField>\n");
586                // sb.append("<returnField>/entity/property/@value</returnField>\n");
587                sb.append("<title>kepler query</title>\n");
588                sb.append("<AND>\n");
589                sb.append("   <condition operator=\"LIKE\" concept=\"/\">%"
590                                + queryString + "%</condition>\n");
591                /// filter out kars that contain WorkflowRun, likely enable this once it's user toggle-able:
592                ///sb.append("   <condition operator=\"NOT LIKE\" concept=\"karEntry/karEntryAttributes/type\">"
593        ///+ WORKFLOWRUNTYPE + "</condition>\n");
594                sb.append("</AND>\n");
595                sb.append("</egq:query>\n");
596                return new StringReader(sb.toString());
597        }
598
599        /**
600         * a class to hold a registry client and a sessionid
601         */
602        public class RegistryClientContainer {
603                public RegistryServiceClient client;
604                public String sessionid;
605
606                /**
607                 * Constructor
608                 */
609                public RegistryClientContainer(RegistryServiceClient client,
610                                String sessionid) {
611                        this.client = client;
612                        this.sessionid = sessionid;
613                }
614        }
615}