Introduce restconf.server.{api,spi,mdsal}
[netconf.git] / restconf / restconf-nb / src / main / java / org / opendaylight / restconf / nb / rfc8040 / OSGiNorthbound.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 static java.util.Objects.requireNonNull;
11
12 import java.util.Map;
13 import org.opendaylight.restconf.nb.rfc8040.streams.DefaultPingExecutor;
14 import org.opendaylight.restconf.nb.rfc8040.streams.DefaultRestconfStreamServletFactory;
15 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
16 import org.opendaylight.restconf.server.mdsal.MdsalRestconfStreamRegistry;
17 import org.osgi.framework.FrameworkUtil;
18 import org.osgi.service.component.ComponentFactory;
19 import org.osgi.service.component.ComponentInstance;
20 import org.osgi.service.component.annotations.Activate;
21 import org.osgi.service.component.annotations.Component;
22 import org.osgi.service.component.annotations.Deactivate;
23 import org.osgi.service.component.annotations.Modified;
24 import org.osgi.service.component.annotations.Reference;
25 import org.osgi.service.metatype.annotations.AttributeDefinition;
26 import org.osgi.service.metatype.annotations.Designate;
27 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * Component managing global RESTCONF northbound configuration.
33  */
34 @Component(service = { }, configurationPid = "org.opendaylight.restconf.nb.rfc8040")
35 @Designate(ocd = OSGiNorthbound.Configuration.class)
36 public final class OSGiNorthbound {
37     @ObjectClassDefinition
38     public @interface Configuration {
39         @AttributeDefinition(min = "0", max = "" + StreamsConfiguration.MAXIMUM_FRAGMENT_LENGTH_LIMIT)
40         int maximum$_$fragment$_$length() default 0;
41         @AttributeDefinition(min = "0")
42         int heartbeat$_$interval() default 10000;
43         @AttributeDefinition(min = "1")
44         int idle$_$timeout() default 30000;
45         @AttributeDefinition(min = "1")
46         String ping$_$executor$_$name$_$prefix() default DefaultPingExecutor.DEFAULT_NAME_PREFIX;
47         // FIXME: this is a misnomer: it specifies the core pool size, i.e. minimum thread count, the maximum is set to
48         //        Integer.MAX_VALUE, which is not what we want
49         @AttributeDefinition(min = "0")
50         int max$_$thread$_$count() default DefaultPingExecutor.DEFAULT_CORE_POOL_SIZE;
51         @AttributeDefinition
52         boolean use$_$sse() default true;
53     }
54
55     private static final Logger LOG = LoggerFactory.getLogger(OSGiNorthbound.class);
56
57     private final ComponentFactory<MdsalRestconfStreamRegistry> registryFactory;
58     private final ComponentFactory<DefaultRestconfStreamServletFactory> servletFactoryFactory;
59
60     private ComponentInstance<MdsalRestconfStreamRegistry> registry;
61     private boolean useSSE;
62
63     private ComponentInstance<DefaultRestconfStreamServletFactory> servletFactory;
64     private Map<String, ?> servletProps;
65
66     @Activate
67     public OSGiNorthbound(
68             @Reference(target = "(component.factory=" + DefaultRestconfStreamServletFactory.FACTORY_NAME + ")")
69             final ComponentFactory<DefaultRestconfStreamServletFactory> servletFactoryFactory,
70             @Reference(target = "(component.factory=" + MdsalRestconfStreamRegistry.FACTORY_NAME + ")")
71             final ComponentFactory<MdsalRestconfStreamRegistry> registryFactory, final Configuration configuration) {
72         this.registryFactory = requireNonNull(registryFactory);
73         this.servletFactoryFactory = requireNonNull(servletFactoryFactory);
74
75         useSSE = configuration.use$_$sse();
76         registry = registryFactory.newInstance(FrameworkUtil.asDictionary(MdsalRestconfStreamRegistry.props(useSSE)));
77
78         servletProps = DefaultRestconfStreamServletFactory.props(registry.getInstance(), useSSE,
79             new StreamsConfiguration(configuration.maximum$_$fragment$_$length(),
80                 configuration.idle$_$timeout(), configuration.heartbeat$_$interval()),
81             configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count());
82         servletFactory = servletFactoryFactory.newInstance(FrameworkUtil.asDictionary(servletProps));
83
84         LOG.info("Global RESTCONF northbound pools started");
85     }
86
87     @Modified
88     void modified(final Configuration configuration) {
89         final var newUseSSE = configuration.use$_$sse();
90         if (newUseSSE != useSSE) {
91             useSSE = newUseSSE;
92             registry.dispose();
93             registry = registryFactory.newInstance(FrameworkUtil.asDictionary(
94                 MdsalRestconfStreamRegistry.props(useSSE)));
95             LOG.debug("ListenersBroker restarted with {}", newUseSSE ? "SSE" : "Websockets");
96         }
97
98         final var newServletProps = DefaultRestconfStreamServletFactory.props(registry.getInstance(), useSSE,
99             new StreamsConfiguration(configuration.maximum$_$fragment$_$length(),
100                 configuration.idle$_$timeout(), configuration.heartbeat$_$interval()),
101             configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count());
102         if (!newServletProps.equals(servletProps)) {
103             servletProps = newServletProps;
104             servletFactory.dispose();
105             servletFactory = servletFactoryFactory.newInstance(FrameworkUtil.asDictionary(servletProps));
106             LOG.debug("RestconfStreamServletFactory restarted with {}", servletProps);
107         }
108
109         LOG.debug("Applied {}", configuration);
110     }
111
112     @Deactivate
113     void deactivate() {
114         servletFactory.dispose();
115         servletFactory = null;
116         registry.dispose();
117         registry = null;
118         LOG.info("Global RESTCONF northbound pools stopped");
119     }
120 }