2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.restconf.nb.rfc8040;
10 import static java.util.Objects.requireNonNull;
13 import org.opendaylight.restconf.api.query.PrettyPrintParam;
14 import org.opendaylight.restconf.nb.rfc8040.streams.DefaultPingExecutor;
15 import org.opendaylight.restconf.nb.rfc8040.streams.DefaultRestconfStreamServletFactory;
16 import org.opendaylight.restconf.nb.rfc8040.streams.StreamsConfiguration;
17 import org.opendaylight.restconf.server.mdsal.MdsalRestconfStreamRegistry;
18 import org.osgi.framework.FrameworkUtil;
19 import org.osgi.service.component.ComponentFactory;
20 import org.osgi.service.component.ComponentInstance;
21 import org.osgi.service.component.annotations.Activate;
22 import org.osgi.service.component.annotations.Component;
23 import org.osgi.service.component.annotations.Deactivate;
24 import org.osgi.service.component.annotations.Modified;
25 import org.osgi.service.component.annotations.Reference;
26 import org.osgi.service.metatype.annotations.AttributeDefinition;
27 import org.osgi.service.metatype.annotations.Designate;
28 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
33 * Component managing global RESTCONF northbound configuration.
35 @Component(service = { }, configurationPid = "org.opendaylight.restconf.nb.rfc8040")
36 @Designate(ocd = OSGiNorthbound.Configuration.class)
37 public final class OSGiNorthbound {
38 @ObjectClassDefinition
39 public @interface Configuration {
40 @AttributeDefinition(min = "0", max = "" + StreamsConfiguration.MAXIMUM_FRAGMENT_LENGTH_LIMIT)
41 int maximum$_$fragment$_$length() default 0;
43 @AttributeDefinition(min = "0")
44 int heartbeat$_$interval() default 10000;
46 @AttributeDefinition(min = "1")
47 int idle$_$timeout() default 30000;
49 @AttributeDefinition(min = "1")
50 String ping$_$executor$_$name$_$prefix() default DefaultPingExecutor.DEFAULT_NAME_PREFIX;
52 // FIXME: this is a misnomer: it specifies the core pool size, i.e. minimum thread count, the maximum is set to
53 // Integer.MAX_VALUE, which is not what we want
54 @AttributeDefinition(min = "0")
55 int max$_$thread$_$count() default DefaultPingExecutor.DEFAULT_CORE_POOL_SIZE;
57 @Deprecated(since = "7.0.0", forRemoval = true)
59 boolean use$_$sse() default true;
61 @AttributeDefinition(name = "{+restconf}", description = """
62 The value of RFC8040 {+restconf} URI template, pointing to the root resource. Must not end with '/'.""")
63 String restconf() default "rests";
66 name = "default pretty-print",
67 description = "Control the default value of the '" + PrettyPrintParam.uriName + "' query parameter.")
68 boolean pretty$_$print() default false;
71 private static final Logger LOG = LoggerFactory.getLogger(OSGiNorthbound.class);
73 private final ComponentFactory<MdsalRestconfStreamRegistry> registryFactory;
74 private final ComponentFactory<DefaultRestconfStreamServletFactory> servletFactoryFactory;
76 private ComponentInstance<MdsalRestconfStreamRegistry> registry;
77 @Deprecated(since = "7.0.0", forRemoval = true)
78 private boolean useSSE;
80 private ComponentInstance<DefaultRestconfStreamServletFactory> servletFactory;
81 private Map<String, ?> servletProps;
84 public OSGiNorthbound(
85 @Reference(target = "(component.factory=" + DefaultRestconfStreamServletFactory.FACTORY_NAME + ")")
86 final ComponentFactory<DefaultRestconfStreamServletFactory> servletFactoryFactory,
87 @Reference(target = "(component.factory=" + MdsalRestconfStreamRegistry.FACTORY_NAME + ")")
88 final ComponentFactory<MdsalRestconfStreamRegistry> registryFactory, final Configuration configuration) {
89 this.registryFactory = requireNonNull(registryFactory);
90 this.servletFactoryFactory = requireNonNull(servletFactoryFactory);
92 useSSE = configuration.use$_$sse();
93 registry = registryFactory.newInstance(FrameworkUtil.asDictionary(MdsalRestconfStreamRegistry.props(useSSE)));
95 servletProps = DefaultRestconfStreamServletFactory.props(configuration.restconf(), registry.getInstance(),
96 PrettyPrintParam.of(configuration.pretty$_$print()), useSSE,
97 new StreamsConfiguration(configuration.maximum$_$fragment$_$length(),
98 configuration.idle$_$timeout(), configuration.heartbeat$_$interval()),
99 configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count());
100 servletFactory = servletFactoryFactory.newInstance(FrameworkUtil.asDictionary(servletProps));
102 LOG.info("Global RESTCONF northbound pools started");
106 void modified(final Configuration configuration) {
107 final var newUseSSE = configuration.use$_$sse();
108 if (newUseSSE != useSSE) {
111 registry = registryFactory.newInstance(FrameworkUtil.asDictionary(
112 MdsalRestconfStreamRegistry.props(useSSE)));
113 LOG.debug("ListenersBroker restarted with {}", newUseSSE ? "SSE" : "Websockets");
115 final var newServletProps = DefaultRestconfStreamServletFactory.props(configuration.restconf(),
116 registry.getInstance(), PrettyPrintParam.of(configuration.pretty$_$print()), useSSE,
117 new StreamsConfiguration(configuration.maximum$_$fragment$_$length(),
118 configuration.idle$_$timeout(), configuration.heartbeat$_$interval()),
119 configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count());
120 if (!newServletProps.equals(servletProps)) {
121 servletProps = newServletProps;
122 servletFactory.dispose();
123 servletFactory = servletFactoryFactory.newInstance(FrameworkUtil.asDictionary(servletProps));
124 LOG.debug("RestconfStreamServletFactory restarted with {}", servletProps);
127 LOG.debug("Applied {}", configuration);
132 servletFactory.dispose();
133 servletFactory = null;
136 LOG.info("Global RESTCONF northbound pools stopped");