Register only one events servlet
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / JaxRsNorthbound.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.restconf.nb.rfc8040;
9
10 import com.google.common.annotations.Beta;
11 import javax.servlet.ServletException;
12 import org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterConfiguration;
13 import org.opendaylight.aaa.filterchain.filters.CustomFilterAdapter;
14 import org.opendaylight.aaa.web.FilterDetails;
15 import org.opendaylight.aaa.web.ServletDetails;
16 import org.opendaylight.aaa.web.WebContext;
17 import org.opendaylight.aaa.web.WebContextSecurer;
18 import org.opendaylight.aaa.web.WebServer;
19 import org.opendaylight.aaa.web.servlet.ServletSupport;
20 import org.opendaylight.controller.config.threadpool.util.NamingThreadPoolFactory;
21 import org.opendaylight.controller.config.threadpool.util.ScheduledThreadPoolWrapper;
22 import org.opendaylight.mdsal.dom.api.DOMActionService;
23 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
24 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
25 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
26 import org.opendaylight.mdsal.dom.api.DOMRpcService;
27 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
28 import org.opendaylight.restconf.nb.rfc8040.databind.DatabindProvider;
29 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.MdsalRestconfServer;
30 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.RestconfDataStreamServiceImpl;
31 import org.opendaylight.restconf.nb.rfc8040.rests.services.impl.SubscribeToStreamUtil;
32 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
33 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
34 import org.opendaylight.restconf.nb.rfc8040.streams.WebSocketInitializer;
35 import org.opendaylight.restconf.nb.rfc8040.streams.listeners.ListenersBroker;
36 import org.opendaylight.yangtools.concepts.Registration;
37 import org.osgi.service.component.annotations.Activate;
38 import org.osgi.service.component.annotations.Component;
39 import org.osgi.service.component.annotations.Deactivate;
40 import org.osgi.service.component.annotations.Reference;
41 import org.osgi.service.metatype.annotations.AttributeDefinition;
42 import org.osgi.service.metatype.annotations.Designate;
43 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
44
45 /**
46  * Main entrypoint into RFC8040 northbound. Take care of wiring up all applications activating them through JAX-RS.
47  */
48 @Beta
49 @Component(service = { }, configurationPid = "org.opendaylight.restconf.nb.rfc8040")
50 @Designate(ocd = JaxRsNorthbound.Configuration.class)
51 public final class JaxRsNorthbound implements AutoCloseable {
52     @ObjectClassDefinition
53     public @interface Configuration {
54         @AttributeDefinition(min = "0", max = "" + StreamsConfiguration.MAXIMUM_FRAGMENT_LENGTH_LIMIT)
55         int maximum$_$fragment$_$length() default 0;
56         @AttributeDefinition(min = "0")
57         int heartbeat$_$interval() default 10000;
58         @AttributeDefinition(min = "1")
59         int idle$_$timeout() default 30000;
60         @AttributeDefinition(min = "1")
61         String ping$_$executor$_$name$_$prefix() default "ping-executor";
62         // FIXME: this is a misnomer: it specifies the core pool size, i.e. minimum thread count, the maximum is set to
63         //        Integer.MAX_VALUE, which is not what we want
64         @AttributeDefinition(min = "0")
65         int max$_$thread$_$count() default 1;
66         @AttributeDefinition
67         boolean use$_$sse() default true;
68     }
69
70     private final Registration discoveryReg;
71     private final Registration restconfReg;
72
73     @Activate
74     public JaxRsNorthbound(@Reference final WebServer webServer, @Reference final WebContextSecurer webContextSecurer,
75             @Reference final ServletSupport servletSupport,
76             @Reference final CustomFilterAdapterConfiguration filterAdapterConfiguration,
77             @Reference final DOMActionService actionService, @Reference final DOMDataBroker dataBroker,
78             @Reference final DOMMountPointService mountPointService,
79             @Reference final DOMNotificationService notificationService, @Reference final DOMRpcService rpcService,
80             @Reference final DOMSchemaService schemaService, @Reference final DatabindProvider databindProvider,
81             @Reference final MdsalRestconfServer server, @Reference final ListenersBroker listenersBroker,
82             final Configuration configuration) throws ServletException {
83         this(webServer, webContextSecurer, servletSupport, filterAdapterConfiguration, actionService, dataBroker,
84             mountPointService, notificationService, rpcService, schemaService, databindProvider, server,
85             listenersBroker, configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count(),
86             new StreamsConfiguration(configuration.maximum$_$fragment$_$length(),
87                 configuration.idle$_$timeout(), configuration.heartbeat$_$interval(), configuration.use$_$sse()));
88     }
89
90     public JaxRsNorthbound(final WebServer webServer, final WebContextSecurer webContextSecurer,
91             final ServletSupport servletSupport, final CustomFilterAdapterConfiguration filterAdapterConfiguration,
92             final DOMActionService actionService, final DOMDataBroker dataBroker,
93             final DOMMountPointService mountPointService, final DOMNotificationService notificationService,
94             final DOMRpcService rpcService, final DOMSchemaService schemaService,
95             final DatabindProvider databindProvider, final MdsalRestconfServer server,
96             final ListenersBroker listenersBroker, final String pingNamePrefix, final int pingMaxThreadCount,
97             final StreamsConfiguration streamsConfiguration) throws ServletException {
98         final var scheduledThreadPool = new ScheduledThreadPoolWrapper(pingMaxThreadCount,
99             new NamingThreadPoolFactory(pingNamePrefix));
100
101         final SubscribeToStreamUtil streamUtils;
102         final ServletDetails streamServlet;
103         if (streamsConfiguration.useSSE()) {
104             streamUtils = SubscribeToStreamUtil.serverSentEvents(listenersBroker);
105             streamServlet = ServletDetails.builder()
106                 .addUrlPattern("/" + URLConstants.SSE_SUBPATH + "/*")
107                 .servlet(servletSupport.createHttpServletBuilder(
108                     new DataStreamApplication(databindProvider,
109                         new RestconfDataStreamServiceImpl(scheduledThreadPool, listenersBroker, streamsConfiguration)))
110                     .build())
111                 .name("notificationServlet")
112                 .asyncSupported(true)
113                 .build();
114         } else {
115             streamUtils = SubscribeToStreamUtil.webSockets(listenersBroker);
116             streamServlet = ServletDetails.builder()
117                 .addUrlPattern("/" + RestconfStreamsConstants.DATA_SUBSCRIPTION + "/*")
118                 .addUrlPattern("/" + RestconfStreamsConstants.NOTIFICATION_STREAM + "/*")
119                 .addUrlPattern("/" + RestconfStreamsConstants.DEVICE_NOTIFICATION_STREAM + "/*")
120                 .servlet(new WebSocketInitializer(scheduledThreadPool, listenersBroker, streamsConfiguration))
121                 .build();
122         }
123
124         final var restconfBuilder = WebContext.builder()
125             .name("RFC8040 RESTCONF")
126             .contextPath("/" + URLConstants.BASE_PATH)
127             .supportsSessions(false)
128             .addServlet(ServletDetails.builder()
129                 .addUrlPattern("/*")
130                 .servlet(servletSupport.createHttpServletBuilder(
131                     new RestconfApplication(databindProvider, server, mountPointService, dataBroker, actionService,
132                         notificationService, schemaService, streamUtils))
133                     .build())
134                 .asyncSupported(true)
135                 .build())
136             .addServlet(streamServlet)
137
138             // Allows user to add javax.servlet.Filter(s) in front of REST services
139             .addFilter(FilterDetails.builder()
140                 .addUrlPattern("/*")
141                 .filter(new CustomFilterAdapter(filterAdapterConfiguration))
142                 .asyncSupported(true)
143                 .build());
144
145         webContextSecurer.requireAuthentication(restconfBuilder, true, "/*");
146
147         restconfReg = webServer.registerWebContext(restconfBuilder.build());
148
149         final var discoveryBuilder = WebContext.builder()
150             .name("RFC6415 Web Host Metadata")
151             .contextPath("/.well-known")
152             .supportsSessions(false)
153             .addServlet(ServletDetails.builder()
154                 .addUrlPattern("/*")
155                 .servlet(servletSupport.createHttpServletBuilder(new RootFoundApplication(URLConstants.BASE_PATH))
156                     .build())
157                 .name("Rootfound")
158                 .build());
159
160         webContextSecurer.requireAuthentication(discoveryBuilder, true, "/*");
161
162         discoveryReg = webServer.registerWebContext(discoveryBuilder.build());
163     }
164
165     @Deactivate
166     @Override
167     public void close() {
168         discoveryReg.close();
169         restconfReg.close();
170     }
171 }