source: trunk/grails-app/services/dbnp/modules/ModuleCommunicationService.groovy @ 1864

Last change on this file since 1864 was 1864, checked in by s.h.sikkema@…, 9 years ago

in exporter: module error display improvements (ie. display on screen but still able to export); removed changes to simplewizard; removed unnecessary imports; should handle 'null' parent subject from samples correctly; should handle non number values from modules correctly; deselects module measurements in case of module error/no measurements; removed obsolete entry in topnav

  • Property svn:keywords set to Rev Author Date
File size: 7.5 KB
Line 
1/**
2 * SynchronizationService Service
3 *
4 * Description of my service
5 *
6 * @author  your email (+name?)
7 * @since       2010mmdd
8 * @package     ???
9 *
10 * Revision information:
11 * $Rev: 1864 $
12 * $Author: s.h.sikkema@gmail.com $
13 * $Date: 2011-05-23 14:36:00 +0000 (ma, 23 mei 2011) $
14 */
15package dbnp.modules
16
17import grails.converters.*
18import javax.servlet.http.HttpServletResponse
19import org.codehaus.groovy.grails.commons.ConfigurationHolder
20import org.hibernate.*;
21
22class ModuleCommunicationService implements Serializable {
23        static transactional = false
24        def authenticationService
25        def moduleNotificationService
26        SessionFactory sessionFactory
27       
28        /**
29         * Cache containing the contents of different URLs. These urls are
30         * saved per user, since the data could be different for different users.
31         */
32        def cache = [:]
33
34        /**
35         * Number of seconds to save the data in cache
36         */
37        def numberOfSecondsInCache = ConfigurationHolder.config.modules.cacheDuration ? Integer.valueOf( ConfigurationHolder.config.modules.cacheDuration.toString() ) : 300;
38
39        /**
40         * Sends a notification to assay modules that some part of a study has changed.
41         *
42         * Only modules that have the notify flag set to true will be notified. They will be notified on the URL
43         *
44         * [moduleUrl]/rest/notifyStudyChange?studyToken=abc
45         *
46         * Errors that occur when calling this URL are ignored. The module itself is responsible of
47         * maintaining a synchronized state.
48         *
49         * @param study
50         * @return
51         */
52        def invalidateStudy( def study ) {
53                moduleNotificationService.invalidateStudy( study );
54        }
55
56        /**
57         * Checks whether a specific method on a module is reachable and returns a SC_OK response code.
58         *
59         * This method will return false if a method returns an error (including 403 and 401 errors) or
60         * a redirect
61         *
62         * @param moduleUrl             URL of the module
63         * @param path                  Path of the rest method on that module. If omitted, the module reachablility itself is tested
64         * @return                              True if the module is reachable, false otherwise
65         */
66        def isModuleReachable(moduleUrl, path = "") {
67                def connection = ( moduleUrl + path ).toURL().openConnection()
68                try {
69                        return connection.responseCode == HttpServletResponse.SC_OK
70                } catch(e) {
71                        return false
72                }
73        }
74
75    /**
76         * Calls a rest method on a module
77         *
78         * @param consumer      Consumer of that specific module
79         * @param restUrl       Full URL for the method to call
80         * @return                      JSON    JSON object of the parsed text
81         * @deprecated          Use callModuleMethod instead
82         */
83        def callModuleRestMethodJSON( consumer, restUrl ) throws Exception {
84                def parts = restUrl.split( /\?/ );
85                def url = "";
86                def query = "";
87               
88                if( parts.size() > 1 ) {
89                        url = parts[ 0 ]
90                        query = parts[ 1 ]
91                } else if( parts.size() > 0 ) { 
92                        url = parts[ 0 ];
93                }
94               
95                return callModuleMethod( consumer, url, query );
96        }
97       
98        /**
99        * Calls a rest method on a module
100        *
101        * @param consumer       Consumer of that specific module
102        * @param restUrl        Full URL for the method to call, without query string
103        * @param args           Query string for the url to call (e.q. token=abc&field=xyz)
104        * @param requestMethod  GET or POST - HTTP request method to use
105        * @return                       JSON    JSON object of the parsed text
106        */
107        def callModuleMethod( String consumer, String restUrl, String args = null, String requestMethod = "GET" ) {
108                if (!authenticationService.isLoggedIn()) {
109                        // should not happen because we can only get here when a user is
110                        // logged in...
111                        throw new Exception('User is not logged in.')
112                }
113
114                // Check whether the url is present in cache
115                def cacheData = retrieveFromCache( restUrl, args );
116                if( cacheData && cacheData[ "success" ] )
117                        return cacheData[ "contents" ];
118                else if( cacheData && !cacheData[ "success" ] )
119                        throw new Exception( "Error while fetching data from " + restUrl + " (from cache): " + cacheData[ "error" ] )
120
121                // create a random session token that will be used to allow to module to
122                // sync with gscf prior to presenting the measurement data
123                def sessionToken = UUID.randomUUID().toString()
124
125                // put the session token to work
126                authenticationService.logInRemotely( consumer, sessionToken, authenticationService.getLoggedInUser() )
127               
128                // Append the sessionToken to the parameters
129                if( !args ) {
130                        args = ""
131                } else {
132                        args += "&"
133                }
134               
135                args += "sessionToken=" + sessionToken
136               
137                // Perform a call to the url
138                def restResponse
139                try {
140                        log.trace "GSCF call (" + requestMethod + ") to " + consumer + " URL: " + restUrl + " (args: " + args + ")"
141
142                        def textResponse
143                        switch( requestMethod.toUpperCase() ) {
144                                case "GET":
145                                        def url = restUrl + "?" + args;
146                                        def connection = url.toURL().openConnection();
147               
148                                        textResponse = url.toURL().getText()
149                               
150                                        break
151                                case "POST":
152                                        def connection = restUrl.toURL().openConnection()
153                                        connection.setRequestMethod( "POST" );
154                                        connection.doOutput = true
155                                       
156                                        def writer = new OutputStreamWriter( connection.outputStream )
157                                        writer.write( args );
158                                        writer.flush()
159                                        writer.close()
160                                       
161                                        connection.connect();
162                                       
163                                        textResponse = connection.content.text
164
165                                        break
166                                default:
167                                        throw new Exception( "Unknown request method given. Use GET or POST" )
168                        }
169
170                        log.trace "GSCF response: " + textResponse
171                        restResponse = JSON.parse( textResponse )
172                } catch (Exception e) {
173                        storeErrorInCache( restUrl, e.getMessage(), args );
174                        throw new Exception( "An error occurred while fetching " + restUrl + ".", e )
175                } finally {
176                        // Dispose of the ephemeral session token
177                        authenticationService.logOffRemotely(consumer, sessionToken)
178                }
179
180                // Store the response in cache
181                storeInCache( restUrl, restResponse, args );
182               
183                return restResponse
184
185        }
186       
187        /**
188         * Checks whether a specific url exists in cache
189         * @param url   URL to call
190         * @return              true if the url is present in cache
191         */
192        def existsInCache( url, args = null ) {
193                return retrieveFromCache( url, args ) == null;
194        }
195
196        /**
197         * Retrieves the contents of a specific URL from cache
198         * @param url   URL to call
199         * @return              JSON object with the contents of the URL or null if the url doesn't exist in cache
200         */
201        def retrieveFromCache( url, args = null ) {
202                def user = authenticationService.getLoggedInUser();
203                def userId = user ? user.id : -1;
204               
205                url = cacheUrl( url, args )
206               
207                if( cache[ userId ] && cache[ userId ][ url ] && ( System.currentTimeMillis() - cache[ userId ][ url ][ "timestamp" ] ) < numberOfSecondsInCache * 1000 ) {
208                        return cache[ userId ][ url ];
209                } else {
210                        return null;
211                }
212        }
213
214        /**
215         * Store the retrieved contents from a url in cache
216         * @param url           URL that has been called
217         * @param contents      Contents of the URL
218         */
219        def storeInCache( url, contents, args = null ) {
220                def user = authenticationService.getLoggedInUser();
221                def userId = user ? user.id : -1;
222
223                if( !cache[ userId ] )
224                        cache[ userId ] = [:]
225
226                cache[ userId ][ cacheUrl( url, args ) ] = [
227                        "timestamp": System.currentTimeMillis(),
228                        "success": true,
229                        "contents": contents
230                ];
231        }
232       
233        /**
234        * Store the retrieved error from a url in cache
235        * @param url            URL that has been called
236        * @param contents       Contents of the URL
237        */
238   def storeErrorInCache( url, error, args = null ) {
239           def user = authenticationService.getLoggedInUser();
240           def userId = user ? user.id : -1;
241
242           if( !cache[ userId ] )
243                   cache[ userId ] = [:]
244
245           cache[ userId ][ cacheUrl( url, args ) ] = [
246                   "timestamp": System.currentTimeMillis(),
247                   "success": false,
248                   "error": error
249           ];
250   }
251   
252   /**
253    * Url used to save data in cache
254    */
255   def cacheUrl( url, args = null ) {
256                if( args ) {
257                        // Remove sessionToken from args
258                        args = args;
259                        def sessionFound = ( args =~ /sessionToken=[^&]/ );
260                        args = sessionFound.replaceAll( "sessionToken=" );
261                       
262                        url += '?' + args
263                }
264                       
265                return url;
266   }
267
268}
Note: See TracBrowser for help on using the repository browser.