source: trunk/grails-app/services/nl/tno/massSequencing/integration/GscfService.groovy @ 70

Last change on this file since 70 was 70, checked in by robert@…, 8 years ago
  • Installed templates (in order to extend session lifetime to 2 hours)
  • Implemented background worker to do work outside the HTTP request
File size: 15.4 KB
Line 
1package nl.tno.massSequencing.integration
2
3import grails.converters.JSON
4import org.codehaus.groovy.grails.commons.ConfigurationHolder
5import org.springframework.web.context.request.RequestContextHolder;
6
7/**
8 * EDP (External Data Provider) for GSCF
9 *
10 * @author Robert Horlings (robert@isdat.nl)
11 * @version 0.9
12 * @see nmcdsp.GscfService
13 */
14class GscfService {
15        def config = ConfigurationHolder.config
16       
17        static transactional = false
18
19        /**
20         * Returns the URL to let the user login at GSCF
21         *
22         * @param params        Parameters of the action called
23         * @param token         Session token
24         * @return                      URL to redirect the user to
25         */
26        public String urlAuthRemote( def params, def token, appendParameters = true ) {
27                def redirectURL = "${config.gscf.baseURL}/login/auth_remote?moduleURL=${this.moduleURL()}&consumer=${this.consumerID()}&token=${token}&"
28                def returnUrl
29               
30                // Append other parameters (but only if this request is a GET request)
31                if( appendParameters && params ) {
32                        def request = RequestContextHolder.getRequestAttributes().getRequest();
33                       
34                        if( request.method != "GET" ) {
35                                // Only GET parameters can be sent to the user. The calling method can call the method again with the appendParameters
36                                // property set to false or without a parameters object.
37                                throw new Exception( "Parameters can only be added in GET requests" );
38                        }
39
40                        returnUrl = config.grails.serverURL
41                        if (params.controller != null){
42                                returnUrl += "/${params.controller}"
43                                if (params.action != null){
44                                        returnUrl += "/${params.action}"
45                                        if (params.id != null){
46                                                returnUrl += "/${params.id}"
47                                        }
48                                }
49                        }
50       
51                        returnUrl += "?" + params.collect { param ->
52                                if( param.key != "controller" && param.key != "action" && param.key != "id" ) {
53                                        if( param.value instanceof List ) {
54                                                return param.value.collect { return param.key.toString().encodeAsURL() + "=" + it.toString().encodeAsURL() }.findAll { it }.join( "&" )
55                                        } else if( param.value instanceof Map ) {
56                                                // Discard this parameter as it is present in other keys as well.
57                                        } else {
58                                                return param.key.toString().encodeAsURL() + "=" + param.value.toString().encodeAsURL();
59                                        }
60                                } else {
61                                        return ""
62                                }
63                        }.findAll { it }.join( "&" );
64                } else {
65                        returnUrl = config.grails.serverURL;
66                }
67
68                // After logging in, send the user to /synchronize/authorization first to synchronize
69                // authorization for this user
70                returnUrl = config.grails.serverURL + '/synchronize/authorization?redirectUrl=' + returnUrl.encodeAsURL()
71               
72                return redirectURL + "returnUrl=" + returnUrl.encodeAsURL();
73        }
74
75        /**
76         * Returns the url to show details of a study in GSCF
77         *
78         * @param study         Study object to view in GSCF
79         * @return                      URL to redirect the user to
80         */
81        public String urlViewStudy( String studyToken ) {
82                return "${config.gscf.baseURL}/study/showByToken/" + studyToken
83        }
84
85        /**
86         * Returns the url to add a new study in GSCF
87         *
88         * @return                      URL to redirect the user to
89         */
90        public String urlAddStudy( String studyToken ) {
91                return config.gscf.baseURL + config.gscf.addStudyPath
92        }
93
94        /**
95        * Returns the URL to register an external search with GSCF
96        *
97        * @return                       URL to redirect the user to
98        */
99   public String urlRegisterSearch( def search ) {
100           return config.gscf.baseURL + config.gscf.registerSearchPath
101   }
102       
103        /**
104         * Retrieves the currently logged in user from GSCF
105         *
106         * @param sessionToken String
107         *
108         * @return Map
109         */
110        public Map getUser(String sessionToken) throws Exception {
111                // Add isAdministrator: false as default, in order to be able to handle old versions of GSCF
112                // that don't provide this information
113                def user = [ isAdministrator: false ]
114                try {
115                        this.callGSCF(sessionToken, "getUser").each {
116                                user[ it.key ] = it.value;
117                        }
118                        return user
119                } catch( Exception e ) {
120                        e.printStackTrace();
121                        return null
122                        //throw new Exception( "Retrieving user details from GSCF failed", e );
123                }
124        }
125
126        /**
127         * Retrieve a list of Studies from the GSCF
128         *
129         * @param sessionToken String
130         *
131         * @return ArrayList
132         */
133        public ArrayList getStudies(String sessionToken) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception {
134                return this.callGSCF(sessionToken, "getStudies")
135        }
136
137
138        /**
139         * Retrieve a list of Studies from the GSCF
140         *
141         * @param sessionToken String
142         *
143         * @return ArrayList
144         */
145        public ArrayList getStudies(String sessionToken, ArrayList studyTokens ) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
146                return this.callGSCF(sessionToken, "getStudies", [ "studyToken": studyTokens ] );
147        }
148
149        /**
150         * Retrieve a single Study from the GSCF
151         *
152         * @param sessionToken String
153         * @param study Study
154         *
155         * @return ArrayList
156         */
157        public def getStudy(String sessionToken, String studyToken) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
158                def list
159
160                try {
161                        list = this.callGSCF(sessionToken, "getStudies", ["studyToken": studyToken])
162                } catch( NotAuthorizedException e ) {
163                        throw new NotAuthorizedException( "User is not authorized to access study with token " + studyToken )
164                } catch( ResourceNotFoundException e ) {
165                        throw new ResourceNotFoundException( "Study with token " + studyToken + " not found in GSCF" )
166                } // Other exceptions are thrown as they appear
167
168                // Return only the first element of the list, since we are only interested in one study
169                if( list?.size() ) {
170                        return list[0];
171                } else {
172                        return []
173                }
174        }
175
176        /**
177         * Retrieve a list of Assays from the GSCF
178         *
179         * @param sessionToken String
180         * @param study Study
181         *
182         * @return ArrayList
183         */
184        public ArrayList getAssays(String sessionToken, String studyToken) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
185                try {
186                        return this.callGSCF(sessionToken, "getAssays", ["studyToken": studyToken])
187                } catch( NotAuthorizedException e ) {
188                        throw new NotAuthorizedException( "User is not authorized to access study with token " + studyToken )
189                } catch( ResourceNotFoundException e ) {
190                        throw new ResourceNotFoundException( "Study with token " + studyToken + " not found in GSCF" )
191                } // Other exceptions are thrown as they appear
192        }
193
194        /**
195         * Retrieve a single Assay from the GSCF
196         *
197         * @param sessionToken String
198         * @param study Study
199         * @param assay Assay
200         *
201         * @return ArrayList
202         */     
203        public def getAssay(String sessionToken, String studyToken, String assayToken )throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
204                def list
205                try {
206                        list = this.callGSCF(sessionToken, "getAssays", ["studyToken": studyToken, "assayToken": assayToken])
207                } catch( NotAuthorizedException e ) {
208                        throw new NotAuthorizedException( "User is not authorized to access study with token " + studyToken )
209                } catch( ResourceNotFoundException e ) {
210                        throw new ResourceNotFoundException( "Assay with token " + assayToken + " not found in GSCF" )
211                } // Other exceptions are thrown as they appear
212
213                // Return only the first element of the list, since we are only interested in one assay
214                if( list.size() == 0 ) {
215                        return []
216                } else {
217                        return list[0];
218                }
219        }
220
221       
222        /**
223         * Retrieve a single Sample from the GSCF
224         *
225         * @param sessionToken String
226         * @param assay Assay
227         * @param sample Sample
228         *
229         * @return ArrayList
230         */
231        public def getSample(String sessionToken, String assayToken, String sampleToken) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
232                def list
233
234                try {
235                        list = this.callGSCF(sessionToken, "getSamples", ["assayToken": assayToken , "sampleToken": sampleToken])
236                } catch( NotAuthorizedException e ) {
237                        throw new NotAuthorizedException( "User is not authorized to access study for assay with token " + assayToken )
238                } catch( ResourceNotFoundException e ) {1
239                        throw new ResourceNotFoundException( "Assay with token " + assayToken + " not found in GSCF" )
240                } // Other exceptions are thrown as they appear
241
242                // Return only the first element of the list, since we are only interested in one sample
243                if( list.size() == 0 )
244                        return []
245                else
246                        return list[ 0 ];
247
248        }
249       
250        /**
251         * Retrieve a list of Samples from the GSCF
252         *
253         * @param sessionToken String
254         * @param assay Assay (parameter assay is optional, only used when you want to limit the list of samples of an Assay within a Study)
255         *
256         * @return ArrayList
257         */
258        public ArrayList getSamples(String sessionToken, String assayToken) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
259                // Samples of a Study limited to a single Assay
260                try {
261                        return this.callGSCF(sessionToken, "getSamples", ["assayToken":assayToken])
262                } catch( NotAuthorizedException e ) {
263                        throw new NotAuthorizedException( "User is not authorized to access study for assay with token " + assayToken )
264                } catch( ResourceNotFoundException e ) {1
265                        throw new ResourceNotFoundException( "Assay with token " + assayToken + " not found in GSCF" )
266                } // Other exceptions are thrown as they appear
267
268        }
269
270        /**
271         * Retrieve a list of samples from the GSCF
272         *
273         * @param sessionToken String
274         * @param sampleTokens List of sampleTokens
275         *
276         * @return ArrayList
277         */
278        public def getSamples(String sessionToken, List sampleTokens) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
279                def list
280
281                try {
282                        list = this.callGSCF(sessionToken, "getSamples", ["sampleToken": sampleTokens])
283                } catch( NotAuthorizedException e ) {
284                        throw new NotAuthorizedException( "User is not authorized to access samples" )
285                } catch( ResourceNotFoundException e ) {
286                        throw new ResourceNotFoundException( "Samples with token " + sampleTokens + " not found in GSCF" )
287                } // Other exceptions are thrown as they appear
288
289                // Return only the first element of the list, since we are only interested in one sample
290                return list
291        }
292
293
294        /**
295         * Retrieve study access
296         *
297         * @param sessionToken String
298         * @param study Study
299         *
300         * @return ArrayList
301         */
302        public HashMap getAuthorizationLevel(String sessionToken, String studyToken) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception  {
303                ArrayList list
304
305                try {
306                        list = this.callGSCF(sessionToken, "getAuthorizationLevel", ["studyToken":studyToken])
307                } catch( ResourceNotFoundException e ) {
308                        throw new ResourceNotFoundException( "Study with token " + studyToken + " not found in GSCF")
309                } // Other exceptions are thrown as they appear
310
311                HashMap map = [:]
312
313                // Convert the list back to a hashmap
314                list.each { entry ->
315                        map[ entry.key ] = entry.value
316                }
317
318                return map
319        }
320
321        /**
322         * Base URL of GSCF Rest Controller/API
323         *
324         * @return      url String
325         */
326        private String restURL() {
327                return "${config.gscf.baseURL}/rest"
328        }
329
330        /**
331         * Call GSCF Service via a secure call
332         *
333         * @param       sessionToken Session token for connection to GSCF
334         * @param       restMethod Method to call on GSCF rest controller
335         * @param       restParams Parameters to provide to the GSCF rest method
336         * @param       requestMethod   Request method to retrieve data
337         *
338         * @return      ArrayList
339         */
340        private ArrayList callGSCF(String sessionToken, String restMethod, HashMap restParams = [:], String requestMethod = "GET" ) throws BadRequestException, NotAuthenticatedException, NotAuthorizedException, ResourceNotFoundException, Exception {
341
342                // Create a string of arguments to send to GSCF
343                def args = "moduleURL=${this.moduleURL()}&consumer=${this.consumerID()}&token=${sessionToken}";
344               
345                // concat all Rest Call specific arguments behind args
346                restParams.each {parameter ->
347                        // If a list is given as value, the parameter should show up multiple times
348                        if( parameter.value instanceof Collection ) {
349                                parameter.value.each { value ->
350                                        args += "&${parameter.key}=" + value.toString().encodeAsURL()
351                                }
352                        } else {
353                                args += "&${parameter.key}=" + parameter.value.toString().encodeAsURL()
354                        }
355                }
356               
357                // construct GSCF address
358                def addr = "${this.restURL()}/${restMethod}"
359                def connection
360               
361                // If the data to be sent is longer than appr 2000 characters, the GET method will fail
362                // For that reason, we switch to POST if needed
363                if( addr.size() + args.size() > 2000 && requestMethod != "POST" ) {
364                        log.warn "Calling " + addr + " with request method POST instead of " + requestMethod + " because content length is too long: " + ( addr.size() + args.size() )
365                        requestMethod = "POST";
366                }
367               
368                // Call GSCF, depending on the requestmethod that is asked for
369                try {
370                        log.info("GSCF REST-CALL (" + requestMethod + "): ${addr}")
371                       
372                        switch( requestMethod.toUpperCase() ) {
373                                case "GET":
374                                        def url = addr + "?" + args;
375                                        connection = url.toURL().openConnection();
376                               
377                                        break
378                                case "POST":
379                                        connection = addr.toURL().openConnection()
380                                        connection.setRequestMethod( "POST" );
381                                        connection.doOutput = true
382                                       
383                                        def writer = new OutputStreamWriter( connection.outputStream )
384                                        writer.write( args );
385                                        writer.flush()
386                                        writer.close()
387                                       
388                                        connection.connect();
389                                       
390                                        break
391                                default:
392                                        throw new Exception( "Unknown request method given. Use GET or POST" )
393                        }
394               
395                } catch( Exception e ) {
396                        log.error("GSCF Call failed when calling service: ${addr}",e)
397                        throw new Exception( "Calling GSCF Rest method ${restMethod} failed. Please check log for more information.", e )
398                }
399               
400                // Handle the response given by GSCF
401                def gscfResponse = []
402
403                switch( connection.responseCode ) {
404                        case 400:       // Bad request
405                                throw new BadRequestException( "Bad request made to GSCF server: " + addr );
406                                break;
407                        case 401:       // Not allowed to access this resource
408                                throw new NotAuthorizedException( "User is not authorized to access the resource " + addr );
409                                break;
410                        case 403:       // Incorrect authentication
411                                println "Not authenticated (" + addr + "): " + connection.responseCode
412                               
413                                // The user is logged in to the metagenomics module, but not to GSCF. We log the user out of the module
414                                RequestContextHolder.currentRequestAttributes().getSession().user = null
415                               
416                                throw new NotAuthenticatedException( "User is not authenticated with GSCF." );
417                                break;
418                        case 404:       // Resource not found
419                                throw new ResourceNotFoundException( "Specified resource could not be found: "  + addr );
420                                break;
421                        case 500:       // Internal server error
422                                throw new Exception( "An unknown error occured when calling service: " + addr + " - " + connection.responseMessage )
423                                break;
424                        default:
425                                try {
426                                        def jsonResponse = JSON.parse(connection.content.text)
427                                        jsonResponse.each { jsonProperty ->
428                                                gscfResponse << jsonProperty
429                                        }
430
431                                        if( jsonResponse.size() > 2000 )
432                                                log.info("GSCF REST-RESP: " + jsonResponse[0..2000] + "..." )
433                                        else 
434                                                log.info("GSCF REST-RESP: " + jsonResponse )
435                                } catch(Exception e) {
436                                        log.error("Parsing GSCF JSON response failed at ${addr}. Reponse was " + connection.content.text,e)
437                                        throw new Exception( "Parsing GSCF JSON response failed at ${addr}.  Please check log for more information.", e )
438                                }
439                                break;
440                }
441
442                return gscfResponse
443        }
444
445        /**
446         * Consumer ID for connection to of GSCF Rest Controller/API
447         *
448         * @return      consumerID      String
449         */
450        private String consumerID() {
451                return config.massSequencing.consumerID
452        }
453
454        /**
455         * Module URL for connection to of GSCF Rest Controller/API
456         *
457         * @return      moduleURL       String
458         */
459        private String moduleURL() {
460                return config.grails.serverURL
461        }
462
463}
Note: See TracBrowser for help on using the repository browser.