View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.protocol.sync;
17  
18  import org.fourthline.cling.UpnpService;
19  import org.fourthline.cling.model.gena.RemoteGENASubscription;
20  import org.fourthline.cling.model.message.StreamRequestMessage;
21  import org.fourthline.cling.model.message.UpnpResponse;
22  import org.fourthline.cling.model.message.gena.IncomingEventRequestMessage;
23  import org.fourthline.cling.model.message.gena.OutgoingEventResponseMessage;
24  import org.fourthline.cling.model.resource.ServiceEventCallbackResource;
25  import org.fourthline.cling.protocol.ReceivingSync;
26  import org.fourthline.cling.model.UnsupportedDataException;
27  import org.fourthline.cling.transport.RouterException;
28  
29  import java.util.logging.Logger;
30  
31  /**
32   * Handles incoming GENA event messages.
33   * <p>
34   * Attempts to find an outgoing (remote) subscription matching the callback and subscription identifier.
35   * Once found, the GENA event message payload will be transformed and the
36   * {@link org.fourthline.cling.model.gena.RemoteGENASubscription#receive(org.fourthline.cling.model.types.UnsignedIntegerFourBytes,
37   * java.util.Collection)} method will be called asynchronously using the executor
38   * returned by {@link org.fourthline.cling.UpnpServiceConfiguration#getRegistryListenerExecutor()}.
39   * </p>
40   *
41   * @author Christian Bauer
42   */
43  public class ReceivingEvent extends ReceivingSync<StreamRequestMessage, OutgoingEventResponseMessage> {
44  
45      final private static Logger log = Logger.getLogger(ReceivingEvent.class.getName());
46  
47      public ReceivingEvent(UpnpService upnpService, StreamRequestMessage inputMessage) {
48          super(upnpService, inputMessage);
49      }
50  
51      protected OutgoingEventResponseMessage executeSync() throws RouterException{
52  
53          if (!getInputMessage().isContentTypeTextUDA()) {
54              log.warning("Received without or with invalid Content-Type: " + getInputMessage());
55              // We continue despite the invalid UPnP message because we can still hope to convert the content
56              // return new StreamResponseMessage(new UpnpResponse(UpnpResponse.Status.UNSUPPORTED_MEDIA_TYPE));
57          }
58  
59          ServiceEventCallbackResource resource =
60                  getUpnpService().getRegistry().getResource(
61                          ServiceEventCallbackResource.class,
62                          getInputMessage().getUri()
63                  );
64  
65          if (resource == null) {
66              log.fine("No local resource found: " + getInputMessage());
67              return new OutgoingEventResponseMessage(new UpnpResponse(UpnpResponse.Status.NOT_FOUND));
68          }
69  
70          final IncomingEventRequestMessage requestMessage =
71                  new IncomingEventRequestMessage(getInputMessage(), resource.getModel());
72  
73          // Error conditions UDA 1.0 section 4.2.1
74          if (requestMessage.getSubscrptionId() == null) {
75              log.fine("Subscription ID missing in event request: " + getInputMessage());
76              return new OutgoingEventResponseMessage(new UpnpResponse(UpnpResponse.Status.PRECONDITION_FAILED));
77          }
78  
79          if (!requestMessage.hasValidNotificationHeaders()) {
80              log.fine("Missing NT and/or NTS headers in event request: " + getInputMessage());
81              return new OutgoingEventResponseMessage(new UpnpResponse(UpnpResponse.Status.BAD_REQUEST));
82          }
83  
84          if (!requestMessage.hasValidNotificationHeaders()) {
85              log.fine("Invalid NT and/or NTS headers in event request: " + getInputMessage());
86              return new OutgoingEventResponseMessage(new UpnpResponse(UpnpResponse.Status.PRECONDITION_FAILED));
87          }
88  
89          if (requestMessage.getSequence() == null) {
90              log.fine("Sequence missing in event request: " + getInputMessage());
91              return new OutgoingEventResponseMessage(new UpnpResponse(UpnpResponse.Status.PRECONDITION_FAILED));
92          }
93  
94          try {
95  
96              getUpnpService().getConfiguration().getGenaEventProcessor().readBody(requestMessage);
97  
98  		} catch (final UnsupportedDataException ex) {
99              log.fine("Can't read event message request body, " + ex);
100 
101             // Pass the parsing failure on to any listeners, so they can take action if necessary
102             final RemoteGENASubscription subscription =
103                 getUpnpService().getRegistry().getRemoteSubscription(requestMessage.getSubscrptionId());
104             if (subscription != null) {
105                 getUpnpService().getConfiguration().getRegistryListenerExecutor().execute(
106                     new Runnable() {
107                         public void run() {
108                             subscription.invalidMessage(ex);
109                         }
110                     }
111                 );
112             }
113 
114             return new OutgoingEventResponseMessage(new UpnpResponse(UpnpResponse.Status.INTERNAL_SERVER_ERROR));
115         }
116 
117         // get the remove subscription, if the subscription can't be found, wait for pending subscription
118         // requests to finish
119         final RemoteGENASubscription subscription =
120                 getUpnpService().getRegistry().getWaitRemoteSubscription(requestMessage.getSubscrptionId());
121 
122         if (subscription == null) {
123             log.severe("Invalid subscription ID, no active subscription: " + requestMessage);
124             return new OutgoingEventResponseMessage(new UpnpResponse(UpnpResponse.Status.PRECONDITION_FAILED));
125         }
126 
127         getUpnpService().getConfiguration().getRegistryListenerExecutor().execute(
128                 new Runnable() {
129                     public void run() {
130                         log.fine("Calling active subscription with event state variable values");
131                         subscription.receive(
132                                 requestMessage.getSequence(),
133                                 requestMessage.getStateVariableValues()
134                         );
135                     }
136                 }
137         );
138 
139         return new OutgoingEventResponseMessage();
140 
141     }
142 }