1 /*
2  * Portions of this file Copyright 1999-2005 University of Chicago
3  * Portions of this file Copyright 1999-2005 The University of Southern California.
4  *
5  * This file or a portion of this file is licensed under the
6  * terms of the Globus Toolkit Public License, found at
7  * http://www.globus.org/toolkit/download/license.html.
8  * If you redistribute this file, with or without
9  * modifications, you must include this notice in the file.
10  */
11
12 #include "globus_common.h"
13 #include "globus_wsrf_resource.h"
14 #include "globus_service_engine.h"
15 #include "globus_service_registry.h"
16 #include "globus_wsrf_core_tools.h"
17 #include "wsnt_NotifyType.h"
18 #include "globus_i_notification_consumer.h"
19 #include "NotificationConsumerService.h"
20 #include "NotificationConsumerService_client.h"
21
22 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
23 /* Prototypes */
24 static
25 int
26 globus_l_notification_consumer_activate(void);
27
28 static
29 int
30 globus_l_notification_consumer_deactivate(void);
31
32 static
33 globus_result_t
34 globus_l_notification_create_resource_id(
35     xsd_any * *                         property,
36     char *                              resource_id);
37
38 /* Local Variables */
39 globus_mutex_t globus_i_notification_lock;
40 globus_cond_t globus_i_notification_cond;
41
42 globus_module_descriptor_t globus_i_notification_consumer_module =
43 {
44     "globus_notification_consumer",
45     globus_l_notification_consumer_activate,
46     globus_l_notification_consumer_deactivate
47 };
48 #endif /* GLOBUS_DONT_DOCUMENT_INTERNAL */
49
50 /* Client Helper API */
51 /**
52  * Create a NotificationConsumerService resource.
53  * @ingroup notification_consumer
54  *
55  * Create a notification consumer resource, which binds an resource endpoint
56  * to a user-defined callback. The @a consumer_reference passed to this 
57  * function will be modified to contain an EPR to this resource which can
58  * be used as the ConsumerReference element in a Subscribe operation call
59  * when using the client stubs for a service which implements the
60  * NotificationProducer API.
61  * 
62  * @param consumer_reference
63  *     EPR to be populated to point to a new NotificationConsumer service
64  *     resource instance.
65  * @param engine
66  *     Service engine which will be used to process requests for this new
67  *     new service.
68  * @param callback
69  *     Pointer to the application callback to be called when a notification
70  *     message is received for this resouce.
71  * @param callback_arg
72  *     Application-specific parameter to the callback function.
73  *
74  * @retval GLOBUS_SUCCESS
75  *     Consumer resource created successfully.
76  * @retval GLOBUS_NOTIFICATION_CONSUMER_ERROR_TYPE_NULL_PARAM
77  *     Null parameter passed to this function.
78  * @retval GLOBUS_NOTIFICATION_CONSUMER_ERROR_TYPE_UNABLE_TO_CREATE_CONSUMER
79  *     Unable to create consumer. Underlying cause will be chained to this
80  *     error, but is typically an out-of-memory situation.
81  */
82 extern
83 globus_result_t
84 globus_notification_create_consumer(
85     wsa_EndpointReferenceType *         consumer_reference,
86     globus_service_engine_t             engine,
87     globus_notification_consumer_func_t callback,
88     void *                              callback_arg)
89 1 {
90 1     globus_result_t                     result;
91 1     globus_uuid_t                       resource_uuid;
92 1     globus_resource_t                   resource;
93 1     globus_i_notification_consumer_t *  consumer;
94 1     xsd_any *                           resource_reference;
95
96 1     if (consumer_reference == NULL || callback == NULL)
97     {
98 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_NULL_PARAM(NULL);
99
100 0         goto out;
101     }
102
103 1     result = globus_uuid_create(&resource_uuid);
104 1     if (result != GLOBUS_SUCCESS)
105     {
106 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNABLE_TO_CREATE_CONSUMER(
107                 globus_error_get(result));
108
109 0         goto out;
110     }
111
112     /* Create a resource which will represent this consumer. We'll associate
113      * the callback/arg with this resource
114      */
115 1     result = globus_resource_create(resource_uuid.text, &resource);
116 1     if (result != GLOBUS_SUCCESS)
117     {
118 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNABLE_TO_CREATE_CONSUMER(
119                 globus_error_get(result));
120 0         goto out;
121     }
122
123 1     consumer = globus_libc_malloc(sizeof(globus_i_notification_consumer_t));
124
125 1     if (consumer == NULL)
126     {
127 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNABLE_TO_CREATE_CONSUMER(
128                 globus_error_get(result));
129
130 0         goto destroy_resource_out;
131     }
132
133 1     consumer->callback = callback;
134 1     consumer->arg = callback_arg;
135 1     consumer->state = CONSUMER_OK;
136
137 1     result = globus_resource_set_resource_specific(
138             resource,
139             consumer,
140             globus_libc_free);
141
142 1     if (result != GLOBUS_SUCCESS)
143     {
144 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNABLE_TO_CREATE_CONSUMER(
145                 globus_error_get(result));
146
147 0         goto free_consumer_out;
148     }
149
150 1     result = globus_l_notification_create_resource_id(
151         &resource_reference,
152         resource_uuid.text);
153
154 1     if (result != GLOBUS_SUCCESS)
155     {
156 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNABLE_TO_CREATE_CONSUMER(
157                 globus_error_get(result));
158
159 0         goto destroy_resource_out;
160     }
161
162 1     result = globus_wsrf_core_create_endpoint_reference(
163             engine,
164             NOTIFICATIONCONSUMERSERVICE_BASE_PATH,
165             &resource_reference,
166             consumer_reference);
167
168 1     if (result != GLOBUS_SUCCESS)
169     {
170 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNABLE_TO_CREATE_CONSUMER(
171                 globus_error_get(result));
172
173 0         goto destroy_resource_out;
174     }
175
176 1     return result;
177
178 free_consumer_out:
179 0     globus_libc_free(consumer);
180 destroy_resource_out:
181 0     globus_resource_destroy(resource);
182 out:
183 0     return result;
184 }
185 /* globus_notification_create_consumer() */
186
187 /**
188  * Destroy a consumer resource.
189  * @ingroup notification_consumer
190  *
191  * The resource associated with the EPR passed to this function will be
192  * destroyed. After this function returns, the callback associated with this
193  * resource will no longer be called. 
194  *
195  * @param consumer_reference
196  *     EPR of the resource to destroy.
197  *
198  * @retval GLOBUS_SUCCESS
199  *     Consumer resource destroyed successfully.
200  * @retval GLOBUS_NOTIFICATION_CONSUMER_ERROR_TYPE_NULL_PARAM
201  *     Null parameter passed to this function.
202  * @retval GLOBUS_NOTIFICATION_CONSUMER_ERROR_TYPE_UNKNOWN_RESOURCE
203  *     The EPR does not refer to a valid resource.
204  */
205 extern
206 globus_result_t
207 globus_notification_destroy_consumer(
208     wsa_EndpointReferenceType *         consumer_reference)
209 0 {
210 0     globus_result_t                     result;
211 0     globus_resource_t                   resource;
212 0     globus_i_notification_consumer_t *  consumer;
213 0     globus_bool_t                       finish_it = GLOBUS_FALSE;
214
215 0     if (consumer_reference == NULL)
216     {
217 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_NULL_PARAM(NULL);
218
219 0         goto out;
220     }
221
222 0     globus_mutex_lock(&globus_i_notification_lock);
223
224 0     result = globus_wsrf_core_get_resource_from_epr(
225             consumer_reference,
226             &resource);
227
228 0     if (result != GLOBUS_SUCCESS || resource == NULL)
229     {
230 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNKNOWN_RESOURCE(
231                 result ? globus_error_get(result) : NULL);
232
233 0         goto unlock_out;
234     }
235
236 0     result = globus_resource_get_resource_specific(
237             resource,
238             (void **)&consumer);
239
240 0     if (result != GLOBUS_SUCCESS || consumer == NULL)
241     {
242 0         result = GLOBUS_NOTIFICATION_CONSUMER_ERROR_UNKNOWN_RESOURCE(
243                 result ? globus_error_get(result) : NULL);
244
245 0         goto finish_out;
246     }
247
248 0     while (consumer->state != CONSUMER_DESTROYED)
249     {
250 0         switch (consumer->state)
251         {
252             case CONSUMER_OK:
253 0                 consumer->state = CONSUMER_DESTROYED;
254 0                 globus_cond_broadcast(&globus_i_notification_cond);
255 0                 finish_it = GLOBUS_TRUE;
256 0                 break;
257
258             case CONSUMER_ACTIVE:
259 0                 finish_it = GLOBUS_TRUE;
260 0                 consumer->state = CONSUMER_ACTIVE_DESTROY_CALLED;
261                 /* FALLSTHROUGH */
262             case CONSUMER_ACTIVE_DESTROY_CALLED:
263 0                 globus_cond_wait(
264                         &globus_i_notification_cond,
265                         &globus_i_notification_lock);
266 0                 break;
267
268             case CONSUMER_DESTROYED:
269 0                 break;
270         }
271     }
272
273 finish_out:
274
275 0     if (finish_it)
276     {
277         /* Destroy resource---consumer is freed by the destroy call */
278 0         result = globus_resource_finish_and_destroy(resource);
279     }
280     else
281     {
282         /* Free reference from this function call */
283 0         result = globus_resource_finish(resource);
284     }
285
286 unlock_out:
287 0     globus_mutex_unlock(&globus_i_notification_lock);
288 out:
289 0     return result;
290 }
291 /* globus_notification_destroy_consumer() */
292
293 #ifndef GLOBUS_DONT_DOCUMENT_INTERNAL
294 static
295 int
296 globus_l_notification_consumer_activate(void)
297 4 {
298 4     globus_module_activate(GLOBUS_COMMON_MODULE);
299 4     globus_module_activate(GLOBUS_SERVICE_ENGINE_MODULE);
300 4     globus_module_activate(GLOBUS_WSRF_RESOURCE_MODULE);
301 4     globus_module_activate(NOTIFICATIONCONSUMERSERVICE_MODULE);
302
303 4     globus_mutex_init(&globus_i_notification_lock, NULL);
304 4     globus_cond_init(&globus_i_notification_cond, NULL);
305
306 4     return GLOBUS_SUCCESS;
307 }
308 /* globus_l_notification_consumer_activate() */
309
310 static
311 int
312 globus_l_notification_consumer_deactivate(void)
313 4 {
314 4     globus_cond_destroy(&globus_i_notification_cond);
315 4     globus_mutex_destroy(&globus_i_notification_lock);
316
317 4     globus_module_deactivate(NOTIFICATIONCONSUMERSERVICE_MODULE);
318 4     globus_module_deactivate(GLOBUS_WSRF_RESOURCE_MODULE);
319 4     globus_module_deactivate(GLOBUS_SERVICE_ENGINE_MODULE);
320 4     globus_module_deactivate(GLOBUS_COMMON_MODULE);
321
322 4     return 0;
323 }
324 /* globus_l_notification_consumer_deactivate() */
325
326 /**
327  * Wrap the resource ID string in an XML any element with the QName
328  * http://www.ibm.com/xmlns/stdwip/web-services/WS-BaseNotification#ResourceId
329  *
330  * @param property
331  *     Pointer to a xsd_any to allocate and populate.
332  * @param resource_id
333  *     String to be used as the ResourceID
334  */
335 static
336 globus_result_t
337 globus_l_notification_create_resource_id(
338     xsd_any * *                         property,
339     char *                              resource_id)
340 1 {
341 1     xsd_string * val;
342 1     xsd_any_init(property);
343
344 1     (*property)->registry = NULL;
345 1     (*property)->any_info = &xsd_string_info;
346 1     (*property)->element = malloc(sizeof(xsd_QName));
347 1     (*property)->element->Namespace =
348             globus_libc_strdup(NOTIFICATION_NS);
349 1     (*property)->element->local = globus_libc_strdup("ResourceID");
350 1     val = (xsd_string *)malloc(sizeof(xsd_string));
351 1     *val = globus_libc_strdup(resource_id);
352 1     (*property)->value = val;
353
354 1     return GLOBUS_SUCCESS;
355 }
356 /* globus_l_notification_create_resource_id() */