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;
17  
18  import org.fourthline.cling.binding.xml.DeviceDescriptorBinder;
19  import org.fourthline.cling.binding.xml.ServiceDescriptorBinder;
20  import org.fourthline.cling.binding.xml.UDA10DeviceDescriptorBinderImpl;
21  import org.fourthline.cling.binding.xml.UDA10ServiceDescriptorBinderImpl;
22  import org.fourthline.cling.model.ModelUtil;
23  import org.fourthline.cling.model.Namespace;
24  import org.fourthline.cling.model.message.UpnpHeaders;
25  import org.fourthline.cling.model.meta.RemoteDeviceIdentity;
26  import org.fourthline.cling.model.meta.RemoteService;
27  import org.fourthline.cling.model.types.ServiceType;
28  import org.fourthline.cling.transport.impl.DatagramIOConfigurationImpl;
29  import org.fourthline.cling.transport.impl.DatagramIOImpl;
30  import org.fourthline.cling.transport.impl.DatagramProcessorImpl;
31  import org.fourthline.cling.transport.impl.GENAEventProcessorImpl;
32  import org.fourthline.cling.transport.impl.MulticastReceiverConfigurationImpl;
33  import org.fourthline.cling.transport.impl.MulticastReceiverImpl;
34  import org.fourthline.cling.transport.impl.NetworkAddressFactoryImpl;
35  import org.fourthline.cling.transport.impl.SOAPActionProcessorImpl;
36  import org.fourthline.cling.transport.impl.StreamClientConfigurationImpl;
37  import org.fourthline.cling.transport.impl.StreamClientImpl;
38  import org.fourthline.cling.transport.impl.StreamServerConfigurationImpl;
39  import org.fourthline.cling.transport.impl.StreamServerImpl;
40  import org.fourthline.cling.transport.spi.DatagramIO;
41  import org.fourthline.cling.transport.spi.DatagramProcessor;
42  import org.fourthline.cling.transport.spi.GENAEventProcessor;
43  import org.fourthline.cling.transport.spi.MulticastReceiver;
44  import org.fourthline.cling.transport.spi.NetworkAddressFactory;
45  import org.fourthline.cling.transport.spi.SOAPActionProcessor;
46  import org.fourthline.cling.transport.spi.StreamClient;
47  import org.fourthline.cling.transport.spi.StreamServer;
48  import org.seamless.util.Exceptions;
49  
50  import javax.enterprise.inject.Alternative;
51  import java.util.concurrent.Executor;
52  import java.util.concurrent.ExecutorService;
53  import java.util.concurrent.RejectedExecutionHandler;
54  import java.util.concurrent.SynchronousQueue;
55  import java.util.concurrent.ThreadFactory;
56  import java.util.concurrent.ThreadPoolExecutor;
57  import java.util.concurrent.TimeUnit;
58  import java.util.concurrent.atomic.AtomicInteger;
59  import java.util.logging.Logger;
60  
61  /**
62   * Default configuration data of a typical UPnP stack.
63   * <p>
64   * This configuration utilizes the default network transport implementation found in
65   * {@link org.fourthline.cling.transport.impl}.
66   * </p>
67   * <p>
68   * This configuration utilizes the DOM default descriptor binders found in
69   * {@link org.fourthline.cling.binding.xml}.
70   * </p>
71   * <p>
72   * The thread <code>Executor</code> is an <code>Executors.newCachedThreadPool()</code> with
73   * a custom {@link ClingThreadFactory} (it only sets a thread name).
74   * </p>
75   * <p>
76   * Note that this pool is effectively unlimited, so the number of threads will
77   * grow (and shrink) as needed - or restricted by your JVM.
78   * </p>
79   * <p>
80   * The default {@link org.fourthline.cling.model.Namespace} is configured without any
81   * base path or prefix.
82   * </p>
83   *
84   * @author Christian Bauer
85   */
86  @Alternative
87  public class DefaultUpnpServiceConfiguration implements UpnpServiceConfiguration {
88  
89      private static Logger log = Logger.getLogger(DefaultUpnpServiceConfiguration.class.getName());
90  
91      final private int streamListenPort;
92  
93      final private ExecutorService defaultExecutorService;
94  
95      final private DatagramProcessor datagramProcessor;
96      final private SOAPActionProcessor soapActionProcessor;
97      final private GENAEventProcessor genaEventProcessor;
98  
99      final private DeviceDescriptorBinder deviceDescriptorBinderUDA10;
100     final private ServiceDescriptorBinder serviceDescriptorBinderUDA10;
101 
102     final private Namespace namespace;
103 
104     /**
105      * Defaults to port '0', ephemeral.
106      */
107     public DefaultUpnpServiceConfiguration() {
108         this(NetworkAddressFactoryImpl.DEFAULT_TCP_HTTP_LISTEN_PORT);
109     }
110 
111     public DefaultUpnpServiceConfiguration(int streamListenPort) {
112         this(streamListenPort, true);
113     }
114 
115     protected DefaultUpnpServiceConfiguration(boolean checkRuntime) {
116         this(NetworkAddressFactoryImpl.DEFAULT_TCP_HTTP_LISTEN_PORT, checkRuntime);
117     }
118 
119     protected DefaultUpnpServiceConfiguration(int streamListenPort, boolean checkRuntime) {
120         if (checkRuntime && ModelUtil.ANDROID_RUNTIME) {
121             throw new Error("Unsupported runtime environment, use org.fourthline.cling.android.AndroidUpnpServiceConfiguration");
122         }
123 
124         this.streamListenPort = streamListenPort;
125 
126         defaultExecutorService = createDefaultExecutorService();
127 
128         datagramProcessor = createDatagramProcessor();
129         soapActionProcessor = createSOAPActionProcessor();
130         genaEventProcessor = createGENAEventProcessor();
131 
132         deviceDescriptorBinderUDA10 = createDeviceDescriptorBinderUDA10();
133         serviceDescriptorBinderUDA10 = createServiceDescriptorBinderUDA10();
134 
135         namespace = createNamespace();
136     }
137 
138     public DatagramProcessor getDatagramProcessor() {
139         return datagramProcessor;
140     }
141 
142     public SOAPActionProcessor getSoapActionProcessor() {
143         return soapActionProcessor;
144     }
145 
146     public GENAEventProcessor getGenaEventProcessor() {
147         return genaEventProcessor;
148     }
149 
150     public StreamClient createStreamClient() {
151         return new StreamClientImpl(
152             new StreamClientConfigurationImpl(
153                 getSyncProtocolExecutorService()
154             )
155         );
156     }
157 
158     public MulticastReceiver createMulticastReceiver(NetworkAddressFactory networkAddressFactory) {
159         return new MulticastReceiverImpl(
160                 new MulticastReceiverConfigurationImpl(
161                         networkAddressFactory.getMulticastGroup(),
162                         networkAddressFactory.getMulticastPort()
163                 )
164         );
165     }
166 
167     public DatagramIO createDatagramIO(NetworkAddressFactory networkAddressFactory) {
168         return new DatagramIOImpl(new DatagramIOConfigurationImpl());
169     }
170 
171     public StreamServer createStreamServer(NetworkAddressFactory networkAddressFactory) {
172         return new StreamServerImpl(
173                 new StreamServerConfigurationImpl(
174                         networkAddressFactory.getStreamListenPort()
175                 )
176         );
177     }
178 
179     public Executor getMulticastReceiverExecutor() {
180         return getDefaultExecutorService();
181     }
182 
183     public Executor getDatagramIOExecutor() {
184         return getDefaultExecutorService();
185     }
186 
187     public ExecutorService getStreamServerExecutorService() {
188         return getDefaultExecutorService();
189     }
190 
191     public DeviceDescriptorBinder getDeviceDescriptorBinderUDA10() {
192         return deviceDescriptorBinderUDA10;
193     }
194 
195     public ServiceDescriptorBinder getServiceDescriptorBinderUDA10() {
196         return serviceDescriptorBinderUDA10;
197     }
198 
199     public ServiceType[] getExclusiveServiceTypes() {
200         return new ServiceType[0];
201     }
202 
203     /**
204      * @return Defaults to <code>false</code>.
205      */
206 	public boolean isReceivedSubscriptionTimeoutIgnored() {
207 		return false;
208 	}
209 
210     public UpnpHeaders getDescriptorRetrievalHeaders(RemoteDeviceIdentity identity) {
211         return null;
212     }
213 
214     public UpnpHeaders getEventSubscriptionHeaders(RemoteService service) {
215         return null;
216     }
217 
218     /**
219      * @return Defaults to 1000 milliseconds.
220      */
221     public int getRegistryMaintenanceIntervalMillis() {
222         return 1000;
223     }
224 
225     /**
226      * @return Defaults to zero, disabling ALIVE flooding.
227      */
228     public int getAliveIntervalMillis() {
229     	return 0;
230     }
231 
232     public Integer getRemoteDeviceMaxAgeSeconds() {
233         return null;
234     }
235 
236     public Executor getAsyncProtocolExecutor() {
237         return getDefaultExecutorService();
238     }
239 
240     public ExecutorService getSyncProtocolExecutorService() {
241         return getDefaultExecutorService();
242     }
243 
244     public Namespace getNamespace() {
245         return namespace;
246     }
247 
248     public Executor getRegistryMaintainerExecutor() {
249         return getDefaultExecutorService();
250     }
251 
252     public Executor getRegistryListenerExecutor() {
253         return getDefaultExecutorService();
254     }
255 
256     public NetworkAddressFactory createNetworkAddressFactory() {
257         return createNetworkAddressFactory(streamListenPort);
258     }
259 
260     public void shutdown() {
261         log.fine("Shutting down default executor service");
262         getDefaultExecutorService().shutdownNow();
263     }
264 
265     protected NetworkAddressFactory createNetworkAddressFactory(int streamListenPort) {
266         return new NetworkAddressFactoryImpl(streamListenPort);
267     }
268 
269     protected DatagramProcessor createDatagramProcessor() {
270         return new DatagramProcessorImpl();
271     }
272 
273     protected SOAPActionProcessor createSOAPActionProcessor() {
274         return new SOAPActionProcessorImpl();
275     }
276 
277     protected GENAEventProcessor createGENAEventProcessor() {
278         return new GENAEventProcessorImpl();
279     }
280 
281     protected DeviceDescriptorBinder createDeviceDescriptorBinderUDA10() {
282         return new UDA10DeviceDescriptorBinderImpl();
283     }
284 
285     protected ServiceDescriptorBinder createServiceDescriptorBinderUDA10() {
286         return new UDA10ServiceDescriptorBinderImpl();
287     }
288 
289     protected Namespace createNamespace() {
290         return new Namespace();
291     }
292 
293     protected ExecutorService getDefaultExecutorService() {
294         return defaultExecutorService;
295     }
296 
297     protected ExecutorService createDefaultExecutorService() {
298         return new ClingExecutor();
299     }
300 
301     public static class ClingExecutor extends ThreadPoolExecutor {
302 
303         public ClingExecutor() {
304             this(new ClingThreadFactory(),
305                  new ThreadPoolExecutor.DiscardPolicy() {
306                      // The pool is unbounded but rejections will happen during shutdown
307                      @Override
308                      public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {
309                          // Log and discard
310                          log.info("Thread pool rejected execution of " + runnable.getClass());
311                          super.rejectedExecution(runnable, threadPoolExecutor);
312                      }
313                  }
314             );
315         }
316 
317         public ClingExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedHandler) {
318             // This is the same as Executors.newCachedThreadPool
319             super(0,
320                   Integer.MAX_VALUE,
321                   60L,
322                   TimeUnit.SECONDS,
323                   new SynchronousQueue<Runnable>(),
324                   threadFactory,
325                   rejectedHandler
326             );
327         }
328 
329         @Override
330         protected void afterExecute(Runnable runnable, Throwable throwable) {
331             super.afterExecute(runnable, throwable);
332             if (throwable != null) {
333                 Throwable cause = Exceptions.unwrap(throwable);
334                 if (cause instanceof InterruptedException) {
335                     // Ignore this, might happen when we shutdownNow() the executor. We can't
336                     // log at this point as the logging system might be stopped already (e.g.
337                     // if it's a CDI component).
338                     return;
339                 }
340                 // Log only
341                 log.warning("Thread terminated " + runnable + " abruptly with exception: " + throwable);
342                 log.warning("Root cause: " + cause);
343             }
344         }
345     }
346 
347     // Executors.DefaultThreadFactory is package visibility (...no touching, you unworthy JDK user!)
348     public static class ClingThreadFactory implements ThreadFactory {
349 
350         protected final ThreadGroup group;
351         protected final AtomicInteger threadNumber = new AtomicInteger(1);
352         protected final String namePrefix = "cling-";
353 
354         public ClingThreadFactory() {
355             SecurityManager s = System.getSecurityManager();
356             group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
357         }
358 
359         public Thread newThread(Runnable r) {
360             Thread t = new Thread(
361                     group, r,
362                     namePrefix + threadNumber.getAndIncrement(),
363                     0
364             );
365             if (t.isDaemon())
366                 t.setDaemon(false);
367             if (t.getPriority() != Thread.NORM_PRIORITY)
368                 t.setPriority(Thread.NORM_PRIORITY);
369 
370             return t;
371         }
372     }
373 
374 }