Remove most RestconfDocumentedException users
[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.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;
31
32 /**
33  * Component managing global RESTCONF northbound configuration.
34  */
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;
42
43         @AttributeDefinition(min = "0")
44         int heartbeat$_$interval() default 10000;
45
46         @AttributeDefinition(min = "1")
47         int idle$_$timeout() default 30000;
48
49         @AttributeDefinition(min = "1")
50         String ping$_$executor$_$name$_$prefix() default DefaultPingExecutor.DEFAULT_NAME_PREFIX;
51
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;
56
57         @Deprecated(since = "7.0.0", forRemoval = true)
58         @AttributeDefinition
59         boolean use$_$sse() default true;
60
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";
64
65         @AttributeDefinition(
66             name = "default pretty-print",
67             description = "Control the default value of the '" + PrettyPrintParam.uriName + "' query parameter.")
68         boolean pretty$_$print() default false;
69
70         @AttributeDefinition(
71             name = "Report 404 on data-missing",
72             description = """
73                 Control the HTTP status code reporting of conditions corresponding to "data-missing". When this is set
74                 to true, the server will violate RFC8040 and report "404" instead of "409".
75
76                 For details and reasoning see https://www.rfc-editor.org/errata/eid5565 and
77                 https://mailarchive.ietf.org/arch/browse/netconf/?gbt=1&index=XcF9r3ek3LvZ4DjF-7_B8kxuiwA""")
78         boolean data$_$missing$_$is$_$404() default false;
79     }
80
81     private static final Logger LOG = LoggerFactory.getLogger(OSGiNorthbound.class);
82
83     private final ComponentFactory<MdsalRestconfStreamRegistry> registryFactory;
84     private final ComponentFactory<DefaultRestconfStreamServletFactory> servletFactoryFactory;
85
86     private ComponentInstance<MdsalRestconfStreamRegistry> registry;
87     @Deprecated(since = "7.0.0", forRemoval = true)
88     private boolean useSSE;
89
90     private ComponentInstance<DefaultRestconfStreamServletFactory> servletFactory;
91     private Map<String, ?> servletProps;
92
93     @Activate
94     public OSGiNorthbound(
95             @Reference(target = "(component.factory=" + DefaultRestconfStreamServletFactory.FACTORY_NAME + ")")
96             final ComponentFactory<DefaultRestconfStreamServletFactory> servletFactoryFactory,
97             @Reference(target = "(component.factory=" + MdsalRestconfStreamRegistry.FACTORY_NAME + ")")
98             final ComponentFactory<MdsalRestconfStreamRegistry> registryFactory, final Configuration configuration) {
99         this.registryFactory = requireNonNull(registryFactory);
100         this.servletFactoryFactory = requireNonNull(servletFactoryFactory);
101
102         useSSE = configuration.use$_$sse();
103         registry = registryFactory.newInstance(FrameworkUtil.asDictionary(MdsalRestconfStreamRegistry.props(useSSE)));
104
105         servletProps = DefaultRestconfStreamServletFactory.props(configuration.restconf(), registry.getInstance(),
106             configuration.data$_$missing$_$is$_$404() ? ErrorTagMapping.ERRATA_5565 : ErrorTagMapping.RFC8040,
107             PrettyPrintParam.of(configuration.pretty$_$print()), useSSE,
108             new StreamsConfiguration(configuration.maximum$_$fragment$_$length(),
109                 configuration.idle$_$timeout(), configuration.heartbeat$_$interval()),
110             configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count());
111         servletFactory = servletFactoryFactory.newInstance(FrameworkUtil.asDictionary(servletProps));
112
113         LOG.info("Global RESTCONF northbound pools started");
114     }
115
116     @Modified
117     void modified(final Configuration configuration) {
118         final var newUseSSE = configuration.use$_$sse();
119         if (newUseSSE != useSSE) {
120             useSSE = newUseSSE;
121             registry.dispose();
122             registry = registryFactory.newInstance(FrameworkUtil.asDictionary(
123                 MdsalRestconfStreamRegistry.props(useSSE)));
124             LOG.debug("ListenersBroker restarted with {}", newUseSSE ? "SSE" : "Websockets");
125         }
126         final var newServletProps = DefaultRestconfStreamServletFactory.props(configuration.restconf(),
127             registry.getInstance(),
128             configuration.data$_$missing$_$is$_$404() ? ErrorTagMapping.ERRATA_5565 : ErrorTagMapping.RFC8040,
129             PrettyPrintParam.of(configuration.pretty$_$print()), useSSE,
130             new StreamsConfiguration(configuration.maximum$_$fragment$_$length(),
131                 configuration.idle$_$timeout(), configuration.heartbeat$_$interval()),
132             configuration.ping$_$executor$_$name$_$prefix(), configuration.max$_$thread$_$count());
133         if (!newServletProps.equals(servletProps)) {
134             servletProps = newServletProps;
135             servletFactory.dispose();
136             servletFactory = servletFactoryFactory.newInstance(FrameworkUtil.asDictionary(servletProps));
137             LOG.debug("RestconfStreamServletFactory restarted with {}", servletProps);
138         }
139
140         LOG.debug("Applied {}", configuration);
141     }
142
143     @Deactivate
144     void deactivate() {
145         servletFactory.dispose();
146         servletFactory = null;
147         registry.dispose();
148         registry = null;
149         LOG.info("Global RESTCONF northbound pools stopped");
150     }
151 }