View Javadoc
1   /*
2    * Copyright (c) 2002-2026, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.notificationstore.service;
35  
36  import com.fasterxml.jackson.core.JsonParseException;
37  import com.fasterxml.jackson.core.JsonProcessingException;
38  import com.fasterxml.jackson.databind.DeserializationFeature;
39  import com.fasterxml.jackson.databind.JsonMappingException;
40  import com.fasterxml.jackson.databind.ObjectMapper;
41  import fr.paris.lutece.plugins.grubusiness.business.customer.Customer;
42  import fr.paris.lutece.plugins.grubusiness.business.demand.Demand;
43  import fr.paris.lutece.plugins.grubusiness.business.demand.IDemandServiceProvider;
44  import fr.paris.lutece.plugins.grubusiness.business.demand.TemporaryStatus;
45  import fr.paris.lutece.plugins.grubusiness.business.notification.*;
46  import fr.paris.lutece.plugins.grubusiness.business.web.rs.EnumGenericStatus;
47  import fr.paris.lutece.plugins.grubusiness.service.notification.INotifierServiceProvider;
48  import fr.paris.lutece.plugins.grubusiness.service.notification.NotificationException;
49  import fr.paris.lutece.plugins.identitystore.web.exception.IdentityStoreException;
50  import fr.paris.lutece.plugins.notificationstore.business.DemandHome;
51  import fr.paris.lutece.plugins.notificationstore.business.NotificationHome;
52  import fr.paris.lutece.plugins.notificationstore.utils.NotificationStoreConstants;
53  import fr.paris.lutece.portal.service.spring.SpringContextService;
54  import fr.paris.lutece.portal.service.util.AppLogService;
55  import fr.paris.lutece.portal.service.util.AppPropertiesService;
56  import org.apache.commons.lang3.StringUtils;
57  
58  import javax.ws.rs.core.Response;
59  import javax.ws.rs.core.Response.Status;
60  import java.io.IOException;
61  import java.time.Instant;
62  import java.time.LocalDateTime;
63  import java.time.ZoneId;
64  import java.time.format.DateTimeFormatter;
65  import java.util.ArrayList;
66  import java.util.List;
67  import java.util.Optional;
68  
69  public class NotificationService
70  {
71  
72      // Bean names
73      private static final String BEAN_STORAGE_SERVICE = "notificationstore.demandService";
74  
75      // Other constants
76      private static final String RESPONSE_OK = "{ \"acknowledge\" : { \"status\": \"received\" } }";
77  
78      private static final String TYPE_DEMAND = "DEMAND";
79      private static final String TYPE_NOTIFICATION = "NOTIFICATION";
80      private static final String STATUS_WARNING = "WARNING";
81      private static final String STATUS_ERROR = "ERROR";
82      private static final String STATUS_FAILED = "FAILED";
83      private static final String STATUS_SUCCESS = "SUCCESS";
84  
85      private static final String TYPE_NOTIFICATION_GUICHET = "GUICHET";
86      private static final String TYPE_NOTIFICATION_AGENT = "AGENT";
87      private static final String TYPE_MERGE_NOTIFICATIONS = "MERGE";
88  
89      // Messages
90      private static final String WARNING_DEMAND_ID_MANDATORY = "Notification Demand_id field is mandatory";
91      private static final String WARNING_DEMAND_TYPE_ID_MANDATORY = "Notification Demand_type_id field is mandatory";
92      private static final String WARNING_CUSTOMER_IDS_MANDATORY = "Valid user connection id or customer id is mandatory";
93      private static final String WARNING_CUSTOMER_NOT_FOUND = "User not found in the identityStore";
94      private static final String ERROR_IDENTITYSTORE = "An error occured while retrieving user from identityStore";
95      private static final String MESSAGE_MISSING_MANDATORY_FIELD = "Missing value";
96      private static final String MESSAGE_MISSING_DEMAND_ID = "Demand Id and Demand type Id are mandatory";
97      private static final String MESSAGE_MISSING_USER_ID = "Valid user connection id or customer id is mandatory";
98      private static final String MESSAGE_INCORRECT_USER = "Incorrect User Ids";
99      private static final String WARNING_INCORRECT_DEMAND_TYPE_ID = "Demand Type Id not found";
100 
101     private static final String PROPERTY_STORE_EVEN_CUSTOMER_ID_NOT_EXISTS = "notificationstore.notification.store.storeEventCustomerIdDoesNotExists";
102 
103     // instance variables
104     private static IDemandServiceProvider _demandService;
105     private static NotificationService _instance;
106     private static List<INotifierServiceProvider> _notifiers;
107 
108     private static ObjectMapper _mapper = new ObjectMapper( );
109 
110     /**
111      * private constructor
112      */
113     private NotificationService( )
114     {
115     }
116 
117     /**
118      * get unique instance of the service
119      * 
120      * @return the notification service
121      */
122     public static NotificationService instance( )
123     {
124         if ( _instance == null )
125         {
126             _instance = new NotificationService( );
127             _demandService = SpringContextService.getBean( BEAN_STORAGE_SERVICE );
128             _notifiers = SpringContextService.getBeansOfType( INotifierServiceProvider.class );
129 
130             _mapper.configure( DeserializationFeature.UNWRAP_ROOT_VALUE, true );
131             _mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
132         }
133 
134         return _instance;
135     }
136 
137     /**
138      * process Notification
139      * 
140      * @param notification
141      */
142     public Response newNotification( String strJson )
143     {
144         List<StatusMessage> warnings = new ArrayList<>( );
145 
146         try
147         {
148             // parse json
149             Notification notification = getNotificationFromJson( strJson );
150 
151             // control customer
152             boolean customerExists = processCustomer( notification, warnings );
153 
154             // check Notification
155             checkNotification( notification, warnings );
156 
157             // store notification only if :
158             // * customer_id is not empty
159             // * AND ( customer exists OR notification can be store even if not exists )
160             boolean storeEvenCustomerIfNotExists = AppPropertiesService.getPropertyBoolean( PROPERTY_STORE_EVEN_CUSTOMER_ID_NOT_EXISTS, false );
161             boolean customerIdNotEmpty = notification.getDemand( ).getCustomer( ) != null
162                     && !StringUtils.isEmpty( notification.getDemand( ).getCustomer( ).getCustomerId( ) );
163 
164             if ( customerIdNotEmpty && ( customerExists || storeEvenCustomerIfNotExists ) )
165             {
166                 store( notification );
167             }
168 
169             // add event (success, or failure if customer not found for example)
170             addEvents( notification, warnings );
171 
172             // forward notification to registred notifiers (if exists)
173             forward( notification );
174 
175         }
176         catch( JsonParseException ex )
177         {
178             return fail( ex, Response.Status.BAD_REQUEST );
179         }
180         catch( JsonMappingException | NullPointerException | NotificationException ex )
181         {
182             return fail( ex, Response.Status.BAD_REQUEST );
183         }
184         catch( IOException ex )
185         {
186             return fail( ex, Response.Status.INTERNAL_SERVER_ERROR );
187         }
188         catch( IdentityStoreException ex )
189         {
190             return fail( ex, Response.Status.INTERNAL_SERVER_ERROR );
191         }
192         catch( Exception ex )
193         {
194             return fail( ex, Response.Status.INTERNAL_SERVER_ERROR );
195         }
196 
197         // success
198         if ( warnings.isEmpty( ) )
199         {
200             return success( );
201         }
202         else
203         {
204             return successWithWarnings( warnings );
205         }
206     }
207 
208     /**
209      * Notification check
210      * 
211      * @param notification
212      * @param warnings
213      */
214     private void checkNotification( Notification notification, List<StatusMessage> warnings )
215     {
216         // notification should be associated to a demand id
217         if ( StringUtils.isBlank( notification.getDemand( ).getId( ) ) )
218         {
219             StatusMessage msg = new StatusMessage( TYPE_DEMAND, STATUS_WARNING, MESSAGE_MISSING_MANDATORY_FIELD, WARNING_DEMAND_ID_MANDATORY );
220             warnings.add( msg );
221         }
222 
223         // notification should be associated to a demand type id
224         if ( StringUtils.isBlank( notification.getDemand( ).getTypeId( ) ) )
225         {
226             StatusMessage msg = new StatusMessage( TYPE_DEMAND, STATUS_WARNING, MESSAGE_MISSING_MANDATORY_FIELD, WARNING_DEMAND_TYPE_ID_MANDATORY );
227             warnings.add( msg );
228         }
229 
230         // check if demand type id exists
231         if ( !_demandService.getDemandType( notification.getDemand( ).getTypeId( ) ).isPresent( ) )
232         {
233             StatusMessage msg = new StatusMessage( TYPE_DEMAND, STATUS_WARNING, MESSAGE_MISSING_DEMAND_ID, WARNING_INCORRECT_DEMAND_TYPE_ID );
234             warnings.add( msg );
235         }
236     }
237 
238     /**
239      * Process customer data
240      * 
241      * @param notification
242      * @param warnings
243      * @throws IdentityStoreException
244      */
245     private boolean processCustomer( Notification notification, List<StatusMessage> warnings ) throws IdentityStoreException
246     {
247 
248         Customer customer = CustomerProvider.instance( ).decrypt( notification.getDemand( ) );
249 
250         // use connection id as customer id (if set in properties, and the other ids are empty)
251         if ( AppPropertiesService.getPropertyBoolean( NotificationStoreConstants.PROPERTY_CONSIDER_GUID_AS_CUSTOMER_ID, false )
252                 && StringUtils.isEmpty( customer.getId( ) ) && StringUtils.isEmpty( customer.getCustomerId( ) )
253                 && !StringUtils.isEmpty( customer.getConnectionId( ) ) )
254         {
255             customer.setCustomerId( customer.getConnectionId( ) );
256         }
257 
258         // check customer identity
259         if ( CustomerProvider.instance( ).hasIdentityService( ) )
260         {
261             // check customer ids
262             if ( customer == null || ( !CustomerProvider.isCustomerIdValid( customer.getCustomerId( ) )
263                     && !CustomerProvider.isCustomerIdValid( customer.getId( ) ) && !CustomerProvider.isConnectionIdValid( customer.getConnectionId( ) ) ) )
264             {
265                 StatusMessage msg = new StatusMessage( TYPE_DEMAND, STATUS_WARNING, MESSAGE_INCORRECT_USER, WARNING_CUSTOMER_IDS_MANDATORY );
266                 warnings.add( msg );
267                 return false;
268             }
269 
270             // set customer id if provided by ID customer attribute (if valid)
271             if ( !CustomerProvider.isCustomerIdValid( customer.getCustomerId( ) ) && CustomerProvider.isCustomerIdValid( customer.getId( ) ) )
272             {
273                 customer.setCustomerId( customer.getId( ) );
274             }
275 
276             try
277             {
278                 // search identity
279                 Customer customerResult = CustomerProvider.instance( ).get( customer.getConnectionId( ), customer.getCustomerId( ) );
280                 if ( customerResult != null )
281                 {
282                     // could be different (in case of consolidated identities for example)
283                     customer.setCustomerId( customerResult.getCustomerId( ) );
284                 }
285                 else
286                 {
287                     // add a warning : the identity does not exists (incorrect ids, identity deleted...)
288                     // A customer must have a valid customer_id of connection_id
289                     StatusMessage msg = new StatusMessage( TYPE_DEMAND, STATUS_WARNING, MESSAGE_INCORRECT_USER, WARNING_CUSTOMER_NOT_FOUND );
290                     warnings.add( msg );
291                     return false;
292                 }
293             }
294             catch( Exception e )
295             {
296                 customer = null;
297                 AppLogService.error( "An error occured while accessing IdentityStore", e );
298                 StatusMessage msg = new StatusMessage( TYPE_DEMAND, STATUS_ERROR, MESSAGE_INCORRECT_USER, ERROR_IDENTITYSTORE );
299                 warnings.add( msg );
300 
301                 throw new IdentityStoreException( "An error occured while accessing IdentityStore", e );
302             }
303         }
304 
305         /*
306          * // reset customer ??? if ( !warnings.isEmpty( ) ) { customer = new Customer( ); customer.setConnectionId( StringUtils.EMPTY );
307          * customer.setCustomerId( StringUtils.EMPTY ); }
308          */
309 
310         notification.getDemand( ).setCustomer( customer );
311 
312         // default
313         return true;
314     }
315 
316     /**
317      * Get a single notification from parameters
318      * 
319      * @param idDemand
320      *            identifier of the demand related to the Notification
321      * @param idDemandType
322      *            identfier of the demand type
323      * @param customerId
324      *            identifier of the customer (CUID) related to the notification
325      * @param notificationType
326      *            Type of the notification
327      * @param notificationDate
328      *            Date of the notification
329      * @return Notification corresponding to the parameters, or else null.
330      */
331     public Notification getNotification( final String idDemand, final String idDemandType, final String customerId, final String notificationType,
332             final long notificationDate )
333     {
334 
335         NotificationFilter filter = new NotificationFilter( );
336         filter.setDemandId( idDemand );
337         filter.setCustomerId( customerId );
338         filter.setDemandTypeId( idDemandType );
339         filter.setStartDate( notificationDate );
340         filter.setEndDate( notificationDate );
341         filter.getListNotificationType( ).add( EnumNotificationType.valueOf( notificationType ) );
342 
343         return NotificationHome.findByFilter( filter ).stream( ).findFirst( ).orElse( null );
344 
345     }
346 
347     /**
348      * Link a notification to another
349      *
350      * @param strJson
351      * @return the response
352      */
353     public Response reassignNotifications( String strJson )
354     {
355         try
356         {
357             _mapper.configure( DeserializationFeature.UNWRAP_ROOT_VALUE, false );
358             ReassignNotificationsRequest request = _mapper.readValue( strJson, ReassignNotificationsRequest.class );
359             _mapper.configure( DeserializationFeature.UNWRAP_ROOT_VALUE, true );
360 
361             AppLogService.debug( "notificationstore / ReassignNotificationsRequest - Received strJson : " + strJson );
362 
363             if ( !CustomerProvider.isCustomerIdValid( request.getOldCustomerId( ) ) || !CustomerProvider.isCustomerIdValid( request.getNewCustomerId( ) ) )
364             {
365                 return fail( new Exception( "Invalid CUIDs" ), Response.Status.BAD_REQUEST );
366             }
367 
368             NotificationFilter filter = new NotificationFilter( );
369             filter.setCustomerId( request.getOldCustomerId( ) );
370             List<Notification> listNotifsToReassign = NotificationHome.getByFilter( filter );
371 
372             if ( listNotifsToReassign.size( ) == 0 )
373             {
374                 return ok( );
375             }
376 
377             DemandHome.reassignDemands( request.getOldCustomerId( ), request.getNewCustomerId( ) );
378             NotificationHome.reassignNotifications( request.getOldCustomerId( ), request.getNewCustomerId( ) );
379 
380             // generate events (for history)
381             for ( Notification notif : listNotifsToReassign )
382             {
383                 notif.getDemand( ).getCustomer( ).setCustomerId( request.getNewCustomerId( ) );
384                 addMergeEvent( notif, request );
385             }
386         }
387         catch( JsonParseException ex )
388         {
389             return fail( ex, Response.Status.BAD_REQUEST );
390         }
391         catch( JsonMappingException | NullPointerException ex )
392         {
393             return fail( ex, Response.Status.BAD_REQUEST );
394         }
395         catch( IOException ex )
396         {
397             return fail( ex, Response.Status.INTERNAL_SERVER_ERROR );
398         }
399         catch( Exception ex )
400         {
401             return fail( ex, Response.Status.INTERNAL_SERVER_ERROR );
402         }
403         return success( );
404     }
405 
406     /**
407      * store a notification event
408      * 
409      * @param strJson
410      * @return the response
411      */
412     public Response newNotificationEvent( String strJson )
413     {
414         try
415         {
416             NotificationEvent notificationEvent = _mapper.readValue( strJson, NotificationEvent.class );
417             AppLogService.debug( "notificationstore / notificationEvent - Received strJson : " + strJson );
418 
419             store( notificationEvent );
420 
421         }
422         catch( JsonParseException ex )
423         {
424             return fail( ex, Response.Status.BAD_REQUEST );
425         }
426         catch( JsonMappingException | NullPointerException ex )
427         {
428             return fail( ex, Response.Status.BAD_REQUEST );
429         }
430         catch( IOException ex )
431         {
432             return fail( ex, Response.Status.INTERNAL_SERVER_ERROR );
433         }
434         catch( Exception ex )
435         {
436             return fail( ex, Response.Status.INTERNAL_SERVER_ERROR );
437         }
438 
439         return success( );
440 
441     }
442 
443     /**
444      * Stores a notification and the associated demand
445      * 
446      * @param notification
447      *            the notification to store
448      */
449     private void store( NotificationEvent notificationEvent )
450     {
451         // set customer id if provided by ID customer attribute (if valid)
452 
453         if ( notificationEvent.getDemand( ) != null && notificationEvent.getDemand( ).getCustomer( ) != null
454                 && !CustomerProvider.isCustomerIdValid( notificationEvent.getDemand( ).getCustomer( ).getCustomerId( ) )
455                 && CustomerProvider.isCustomerIdValid( notificationEvent.getDemand( ).getCustomer( ).getId( ) ) )
456         {
457             notificationEvent.getDemand( ).getCustomer( ).setCustomerId( notificationEvent.getDemand( ).getCustomer( ).getId( ) );
458         }
459 
460         _demandService.create( notificationEvent );
461     }
462 
463     /**
464      * Stores a notification and the associated demand
465      * 
466      * @param notification
467      *            the notification to store
468      */
469     private void store( Notification notification )
470     {
471         Demand demand = _demandService.findByPrimaryKey( notification.getDemand( ).getId( ), notification.getDemand( ).getTypeId( ),
472                 notification.getDemand( ).getCustomer( ).getCustomerId( ) );
473 
474         if ( demand == null || ( demand.getCustomer( ) != null && demand.getCustomer( ).getCustomerId( ) != null
475                 && !demand.getCustomer( ).getCustomerId( ).equals( notification.getDemand( ).getCustomer( ).getCustomerId( ) ) ) )
476         {
477             demand = new Demand( );
478 
479             demand.setId( notification.getDemand( ).getId( ) );
480             demand.setTypeId( notification.getDemand( ).getTypeId( ) );
481             demand.setSubtypeId( notification.getDemand( ).getSubtypeId( ) );
482             demand.setReference( notification.getDemand( ).getReference( ) );
483             demand.setCreationDate( notification.getDate( ) );
484             demand.setMaxSteps( notification.getDemand( ).getMaxSteps( ) );
485             demand.setCurrentStep( notification.getDemand( ).getCurrentStep( ) );
486             demand.setStatusId( getNewDemandStatusIdFromNotification( notification ) );
487             demand.setMetaData( notification.getDemand( ).getMetaData( ) );
488 
489             Customer customerDemand = new Customer( );
490             customerDemand.setCustomerId( notification.getDemand( ).getCustomer( ).getId( ) );
491             customerDemand.setCustomerId( notification.getDemand( ).getCustomer( ).getCustomerId( ) );
492             customerDemand.setConnectionId( notification.getDemand( ).getCustomer( ).getConnectionId( ) );
493             demand.setCustomer( customerDemand );
494 
495             // create demand
496             _demandService.create( demand );
497         }
498         else
499         {
500             // update demand status
501             demand.setCurrentStep( notification.getDemand( ).getCurrentStep( ) );
502 
503             demand.setModifyDate( notification.getDate( ) );
504 
505             int nNewStatusId = getNewDemandStatusIdFromNotification( notification );
506 
507             demand.setStatusId( nNewStatusId );
508 
509             EnumGenericStatus oldStatus = EnumGenericStatus.getByStatusId( demand.getStatusId( ) );
510             EnumGenericStatus newStatus = EnumGenericStatus.getByStatusId( nNewStatusId );
511 
512             // Demand opened to closed
513             if ( oldStatus != null && newStatus != null && !oldStatus.isFinalStatus( ) && newStatus.isFinalStatus( ) )
514             {
515                 demand.setClosureDate( notification.getDate( ) );
516             }
517 
518             // Demand closed to opened
519             if ( oldStatus != null && newStatus != null && oldStatus.isFinalStatus( ) && !newStatus.isFinalStatus( ) )
520             {
521                 demand.setClosureDate( 0 );
522             }
523 
524             _demandService.update( demand );
525         }
526         notification.setDemand( demand );
527 
528         // create notification
529         _demandService.create( notification );
530     }
531 
532     /**
533      * Values and store the NotificationEvent object if failure
534      * 
535      * @param notification
536      * @param warnings
537      * @param strMessage
538      */
539     private void addEvents( Notification notification, List<StatusMessage> warnings )
540     {
541         // no event
542         if ( warnings.isEmpty( ) )
543         {
544             return;
545         }
546 
547         Event event = new Event( );
548         event.setEventDate( notification.getDate( ) );
549 
550         if ( notification.getMyDashboardNotification( ) != null )
551         {
552             event.setType( TYPE_NOTIFICATION_GUICHET );
553         }
554         else
555         {
556             event.setType( TYPE_NOTIFICATION_AGENT );
557         }
558 
559         event.setMessage( generateEventMessage( notification, warnings ) );
560         event.setStatus( STATUS_FAILED );
561 
562         NotificationEvent notificationEvent = new NotificationEvent( );
563         notificationEvent.setEvent( event );
564         notificationEvent.setMsgId( StringUtils.EMPTY );
565         notificationEvent.setDemand( notification.getDemand( ) );
566         notificationEvent.setNotificationDate( notification.getDate( ) );
567 
568         store( notificationEvent );
569     }
570 
571     /**
572      * Values and store the NotificationEvent object if failure
573      * 
574      * @param notification
575      * @param warnings
576      * @param strMessage
577      */
578     private void addMergeEvent( Notification notification, ReassignNotificationsRequest request )
579     {
580         Event event = new Event( );
581         event.setEventDate( notification.getDate( ) );
582 
583         event.setType( TYPE_MERGE_NOTIFICATIONS );
584 
585         event.setMessage( "Merged CUID : " + request.getOldCustomerId( ) + "\nConsolidated CUID : " + request.getNewCustomerId( ) );
586         event.setStatus( STATUS_WARNING );
587 
588         NotificationEvent notificationEvent = new NotificationEvent( );
589         notificationEvent.setEvent( event );
590         notificationEvent.setMsgId( StringUtils.EMPTY );
591         notificationEvent.setDemand( notification.getDemand( ) );
592         notificationEvent.setNotificationDate( notification.getDate( ) );
593 
594         store( notificationEvent );
595     }
596 
597     /**
598      * Build an error response
599      * 
600      * @param strMessage
601      *            The error message
602      * @param ex
603      *            An exception
604      * @return The response
605      */
606     private Response successWithWarnings( List<StatusMessage> warnings )
607     {
608         StringBuilder strWarnings = new StringBuilder( "[" );
609 
610         if ( warnings != null )
611         {
612             for ( StatusMessage msg : warnings )
613             {
614                 strWarnings.append( msg.asJson( ) ).append( "," );
615             }
616 
617             // remove last ","
618             strWarnings.setLength( strWarnings.length( ) - 1 );
619         }
620 
621         strWarnings.append( "]" );
622 
623         String strResponse = "{ \"acknowledge\" : { \"status\": \"warning\", \"warnings\" : " + strWarnings.toString( ) + " } }";
624 
625         return Response.status( Response.Status.CREATED ).entity( strResponse ).build( );
626     }
627 
628     /**
629      * success case
630      * 
631      * @return a successful response
632      */
633     private Response success( )
634     {
635         return Response.status( Response.Status.CREATED ).entity( RESPONSE_OK ).build( );
636     }
637 
638     /**
639      * ok case
640      * 
641      * @return a successful response
642      */
643     private Response ok( )
644     {
645         return Response.status( Response.Status.OK ).entity( RESPONSE_OK ).build( );
646     }
647 
648     /**
649      * Build an error response
650      * 
651      * @param strMessage
652      *            The error message
653      * @param ex
654      *            An exception
655      * @return The response
656      */
657     private Response fail( Throwable ex, Status httpStatus )
658     {
659         StringBuilder strMsg = new StringBuilder( "[" );
660 
661         if ( ex != null )
662         {
663             AppLogService.error( ex.getMessage( ), ex );
664             strMsg.append( new StatusMessage( TYPE_NOTIFICATION, STATUS_ERROR, ex.toString( ), ex.getMessage( ) ).asJson( ) );
665         }
666 
667         strMsg.append( "]" );
668         String strError = "{ \"acknowledge\" : { \"status\": \"error\", \"errors\" : " + strMsg + " } }";
669 
670         return Response.status( httpStatus ).entity( strError ).build( );
671     }
672 
673     /**
674      * Generates the error message
675      * 
676      * @param notification
677      * @param strResponseStatus
678      * @param strErrorMessage
679      * @return
680      */
681     private String generateEventMessage( Notification notification, List<StatusMessage> warnings )
682     {
683         StringBuilder message = new StringBuilder( );
684         message.append( "WARNINGS\n" );
685         message.append( "\n" );
686         message.append( "Demande id : " ).append( notification.getDemand( ).getId( ) ).append( "\n" );
687         message.append( "Demande Type id : " ).append( notification.getDemand( ).getTypeId( ) ).append( "\n" );
688         message.append( "Notification date : " ).append( Instant.ofEpochSecond( notification.getDate( ) ) ).append( "\n" );
689         message.append( "\n" );
690 
691         if ( notification.getDemand( ).getCustomer( ) != null )
692         {
693             message.append( "Customer id: " ).append( notification.getDemand( ).getCustomer( ).getCustomerId( ) ).append( "\n" );
694             message.append( "Connection id: " ).append( notification.getDemand( ).getCustomer( ).getConnectionId( ) ).append( "\n" );
695         }
696         message.append( "-------------------------\n" );
697 
698         for ( StatusMessage s : warnings )
699         {
700             message.append( "Type : " ).append( s.getType( ) ).append( "\n" );
701             message.append( "Status : " ).append( s.getStatus( ) ).append( "\n" );
702             message.append( "Message : " ).append( s.getStrMessage( ) ).append( "\n" );
703             message.append( "Reason : " ).append( s.getReason( ) ).append( "\n" );
704             message.append( "-------------------------\n" );
705 
706         }
707 
708         return message.toString( );
709     }
710 
711     /**
712      * Calculates the generic status id for new notifications.
713      * 
714      * @param notification
715      * @return
716      */
717     private int getNewDemandStatusIdFromNotification( Notification notification )
718     {
719         // consider first the status sent in the demand
720         if ( notification.getDemand( ) != null && notification.getDemand( ).getStatusId( ) > 0
721                 && EnumGenericStatus.exists( notification.getDemand( ).getStatusId( ) ) )
722         {
723             return notification.getDemand( ).getStatusId( );
724         }
725 
726         // Otherwise, consider the MyDashBoard notification status id
727         if ( notification.getMyDashboardNotification( ) != null )
728         {
729             if ( notification.getMyDashboardNotification( ).getStatusId( ) > 0
730                     && EnumGenericStatus.exists( notification.getMyDashboardNotification( ).getStatusId( ) ) )
731             {
732                 return notification.getMyDashboardNotification( ).getStatusId( );
733             }
734 
735             // Otherwise, try to guess the status id from the label
736             Optional<TemporaryStatus> status = _demandService.getStatusByLabel( notification.getMyDashboardNotification( ).getStatusText( ) );
737             if ( status.isPresent( ) && status.get( ).getGenericStatus( ) != null )
738             {
739                 return status.get( ).getGenericStatus( ).getStatusId( );
740             }
741         }
742 
743         // default
744         return -1;
745     }
746 
747     /**
748      * parse json
749      * 
750      * @param strJson
751      * @return the notification
752      * @throws JsonMappingException
753      * @throws JsonProcessingException
754      */
755     private Notification getNotificationFromJson( String strJson ) throws JsonMappingException, JsonProcessingException
756     {
757         AppLogService.debug( "notificationstore / notification - Received strJson : " + strJson );
758 
759         // Format from JSON
760         ObjectMapper mapper = new ObjectMapper( );
761         mapper.configure( DeserializationFeature.UNWRAP_ROOT_VALUE, true );
762         mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
763 
764         Notification notification = mapper.readValue( strJson, Notification.class );
765 
766         return notification;
767     }
768 
769     /**
770      * call the registred notifyers
771      * 
772      * @param notification
773      * @throws NotificationException
774      */
775     public void forward( Notification notification ) throws NotificationException
776     {
777 
778         for ( INotifierServiceProvider notifier : _notifiers )
779         {
780             notifier.process( notification );
781         }
782 
783     }
784 }