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

Last change on this file since 1576 was 1576, checked in by robert@…, 10 years ago
  • Solved locking problems with module communication
  • Fixed a null pointer bug in assayController
  • Property svn:keywords set to Rev Author Date
File size: 6.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: 1576 $
12 * $Author: robert@isdat.nl $
13 * $Date: 2011-02-28 16:44:13 +0000 (ma, 28 feb 2011) $
14 */
15package dbnp.modules
16
17import dbnp.studycapturing.*
18import dbnp.authentication.*
19import grails.converters.*
20import javax.servlet.http.HttpServletResponse
21import org.codehaus.groovy.grails.commons.ConfigurationHolder
22import org.hibernate.*;
23
24class ModuleCommunicationService implements Serializable {
25        static transactional = false
26        def authenticationService
27        def moduleNotificationService
28        SessionFactory sessionFactory
29       
30        /**
31         * Cache containing the contents of different URLs. These urls are
32         * saved per user, since the data could be different for different users.
33         */
34        def cache = [:]
35
36        /**
37         * Number of seconds to save the data in cache
38         */
39        def numberOfSecondsInCache = ConfigurationHolder.config.modules.cacheDuration ? Integer.valueOf( ConfigurationHolder.config.modules.cacheDuration.toString() ) : 300;
40
41        /**
42         * Sends a notification to assay modules that some part of a study has changed.
43         *
44         * Only modules that have the notify flag set to true will be notified. They will be notified on the URL
45         *
46         * [moduleUrl]/rest/notifyStudyChange?studyToken=abc
47         *
48         * Errors that occur when calling this URL are ignored. The module itself is responsible of
49         * maintaining a synchronized state.
50         *
51         * @param study
52         * @return
53         */
54        def invalidateStudy( def study ) {
55                moduleNotificationService.invalidateStudy( study );
56        }
57
58        /**
59         * Checks whether a specific method on a module is reachable and returns a SC_OK response code.
60         *
61         * This method will return false if a method returns an error (including 403 and 401 errors) or
62         * a redirect
63         *
64         * @param moduleUrl             URL of the module
65         * @param path                  Path of the rest method on that module. If omitted, the module reachablility itself is tested
66         * @return                              True if the module is reachable, false otherwise
67         */
68        def isModuleReachable(moduleUrl, path = "") {
69                def connection = ( moduleUrl + path ).toURL().openConnection()
70                try {
71                        return connection.responseCode == HttpServletResponse.SC_OK
72                } catch(e) {
73                        return false
74                }
75        }
76
77        /**
78         * Calls a rest method on a module
79         *
80         * @param consumer      Consumer of that specific module
81         * @param restUrl       Full URL for the method to call
82         * @return                      JSON    JSON object of the parsed text
83         */
84        def callModuleRestMethodJSON( consumer, restUrl ) throws Exception {
85                if (!authenticationService.isLoggedIn()) {
86                        // should not happen because we can only get here when a user is
87                        // logged in...
88                        throw new Exception('User is not logged in.')
89                }
90
91                // Check whether the url is present in cache
92                def cacheData = retrieveFromCache( restUrl );
93                if( cacheData && cacheData[ "success" ] )
94                        return cacheData[ "contents" ];
95                else if( cacheData && !cacheData[ "success" ] )
96                        throw new Exception( "Error while fetching data from " + restUrl + " (from cache): " + cacheData[ "error" ] )
97
98                // create a random session token that will be used to allow to module to
99                // sync with gscf prior to presenting the measurement data
100                def sessionToken = UUID.randomUUID().toString()
101
102                // put the session token to work
103                // This saving is done in a separate session, since that seems to be the only way to have grails/hibernate
104                // really save the object to the database. This is needed, since the module will start a new http request to GSCF
105                // and in that request the database object must exist.
106                // Using session.flush(), save(flush:true) or transaction.commit() don't do the trick. If you know
107                // a better way to perform this trick, feel free to change it :)
108                def hibernateSession = sessionFactory.openSession( sessionFactory.getCurrentSession().connection() );
109                def transaction = hibernateSession.beginTransaction();
110               
111                if( transaction ) {
112                        authenticationService.logInRemotely( consumer, sessionToken, authenticationService.getLoggedInUser() )
113                        transaction.commit();
114                }
115                hibernateSession.flush();
116               
117                // Append the sessionToken to the URL
118                def url = restUrl
119                if( restUrl.indexOf( '?' ) > 0 ) {
120                        // The url itself also has parameters
121                        url += '&sessionToken=' + sessionToken
122                } else {
123                        // The url itself doesn't have parameters
124                        url += '?sessionToken=' + sessionToken
125                }
126               
127                // Perform a call to the url
128                def restResponse
129                try {
130                        def textResponse = url.toURL().getText()
131                        log.trace "GSCF call to " + consumer + " URL: " + url
132                        log.trace "GSCF response: " + textResponse
133                        restResponse = JSON.parse( textResponse )
134                } catch (Exception e) {
135                        storeErrorInCache( restUrl, e.getMessage() );
136                        throw new Exception( "An error occurred while fetching " + url + ".", e )
137                } finally {
138                        // Dispose of the ephemeral session token
139                        //authenticationService.logOffRemotely(consumer, sessionToken)
140                }
141
142                // Store the response in cache
143                storeInCache( restUrl, restResponse );
144               
145                return restResponse
146        }
147
148        /**
149         * Checks whether a specific url exists in cache
150         * @param url   URL to call
151         * @return              true if the url is present in cache
152         */
153        def existsInCache( url ) {
154                return retrieveFromCache( url ) == null;
155        }
156
157        /**
158         * Retrieves the contents of a specific URL from cache
159         * @param url   URL to call
160         * @return              JSON object with the contents of the URL or null if the url doesn't exist in cache
161         */
162        def retrieveFromCache( url ) {
163                def user = authenticationService.getLoggedInUser();
164                def userId = user ? user.id : -1;
165               
166                if( cache[ userId ] && cache[ userId ][ url ] && ( System.currentTimeMillis() - cache[ userId ][ url ][ "timestamp" ] ) < numberOfSecondsInCache * 1000 ) {
167                        return cache[ userId ][ url ];
168                } else {
169                        return null;
170                }
171        }
172
173        /**
174         * Store the retrieved contents from a url in cache
175         * @param url           URL that has been called
176         * @param contents      Contents of the URL
177         */
178        def storeInCache( url, contents ) {
179                def user = authenticationService.getLoggedInUser();
180                def userId = user ? user.id : -1;
181
182                if( !cache[ userId ] )
183                        cache[ userId ] = [:]
184
185                cache[ userId ][ url ] = [
186                        "timestamp": System.currentTimeMillis(),
187                        "success": true,
188                        "contents": contents
189                ];
190        }
191       
192        /**
193        * Store the retrieved error from a url in cache
194        * @param url            URL that has been called
195        * @param contents       Contents of the URL
196        */
197   def storeErrorInCache( url, error ) {
198           def user = authenticationService.getLoggedInUser();
199           def userId = user ? user.id : -1;
200
201           if( !cache[ userId ] )
202                   cache[ userId ] = [:]
203
204           cache[ userId ][ url ] = [
205                   "timestamp": System.currentTimeMillis(),
206                   "success": false,
207                   "error": error
208           ];
209   }
210
211}
Note: See TracBrowser for help on using the repository browser.