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

Last change on this file since 1991 was 1991, checked in by robert@…, 12 years ago

Updated visualization controller and added log debug statements to module communication service

  • Property svn:keywords set to Rev Author Date
File size: 7.6 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: 1991 $
12 * $Author: robert@isdat.nl $
13 * $Date: 2011-09-02 10:37:09 +0000 (vr, 02 sep 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                                        log.trace( "Using GET method" );
146                                        def url = restUrl + "?" + args;
147                                        def connection = url.toURL().openConnection();
148               
149                                        textResponse = url.toURL().getText()
150                               
151                                        break
152                                case "POST":
153                                        log.trace( "Using POST method" );
154                                        def connection = restUrl.toURL().openConnection()
155                                        connection.setRequestMethod( "POST" );
156                                        connection.doOutput = true
157                                       
158                                        def writer = new OutputStreamWriter( connection.outputStream )
159                                        writer.write( args );
160                                        writer.flush()
161                                        writer.close()
162                                       
163                                        connection.connect();
164                                       
165                                        textResponse = connection.content.text
166
167                                        break
168                                default:
169                                        throw new Exception( "Unknown request method given. Use GET or POST" )
170                        }
171
172                        log.trace "GSCF response: " + textResponse
173                        restResponse = JSON.parse( textResponse )
174                } catch (Exception e) {
175                        storeErrorInCache( restUrl, e.getMessage(), args );
176                        throw new Exception( "An error occurred while fetching " + restUrl + ".", e )
177                } finally {
178                        // Dispose of the ephemeral session token
179                        authenticationService.logOffRemotely(consumer, sessionToken)
180                }
181
182                // Store the response in cache
183                storeInCache( restUrl, restResponse, args );
184               
185                return restResponse
186
187        }
188       
189        /**
190         * Checks whether a specific url exists in cache
191         * @param url   URL to call
192         * @return              true if the url is present in cache
193         */
194        def existsInCache( url, args = null ) {
195                return retrieveFromCache( url, args ) == null;
196        }
197
198        /**
199         * Retrieves the contents of a specific URL from cache
200         * @param url   URL to call
201         * @return              JSON object with the contents of the URL or null if the url doesn't exist in cache
202         */
203        def retrieveFromCache( url, args = null ) {
204                def user = authenticationService.getLoggedInUser();
205                def userId = user ? user.id : -1;
206               
207                url = cacheUrl( url, args )
208               
209                if( cache[ userId ] && cache[ userId ][ url ] && ( System.currentTimeMillis() - cache[ userId ][ url ][ "timestamp" ] ) < numberOfSecondsInCache * 1000 ) {
210                        return cache[ userId ][ url ];
211                } else {
212                        return null;
213                }
214        }
215
216        /**
217         * Store the retrieved contents from a url in cache
218         * @param url           URL that has been called
219         * @param contents      Contents of the URL
220         */
221        def storeInCache( url, contents, args = null ) {
222                def user = authenticationService.getLoggedInUser();
223                def userId = user ? user.id : -1;
224
225                if( !cache[ userId ] )
226                        cache[ userId ] = [:]
227
228                cache[ userId ][ cacheUrl( url, args ) ] = [
229                        "timestamp": System.currentTimeMillis(),
230                        "success": true,
231                        "contents": contents
232                ];
233        }
234       
235        /**
236        * Store the retrieved error from a url in cache
237        * @param url            URL that has been called
238        * @param contents       Contents of the URL
239        */
240   def storeErrorInCache( url, error, args = null ) {
241           def user = authenticationService.getLoggedInUser();
242           def userId = user ? user.id : -1;
243
244           if( !cache[ userId ] )
245                   cache[ userId ] = [:]
246
247           cache[ userId ][ cacheUrl( url, args ) ] = [
248                   "timestamp": System.currentTimeMillis(),
249                   "success": false,
250                   "error": error
251           ];
252   }
253   
254   /**
255    * Url used to save data in cache
256    */
257   def cacheUrl( url, args = null ) {
258                if( args ) {
259                        // Remove sessionToken from args
260                        args = args;
261                        def sessionFound = ( args =~ /sessionToken=[^&]/ );
262                        args = sessionFound.replaceAll( "sessionToken=" );
263                       
264                        url += '?' + args
265                }
266                       
267                return url;
268   }
269
270}
Note: See TracBrowser for help on using the repository browser.