Convert mdsal-binding-dom-codec-osgi to a JPMS module
[mdsal.git] / binding / mdsal-binding-dom-codec-osgi / src / main / java / org / opendaylight / mdsal / binding / dom / codec / osgi / impl / OSGiBindingDOMCodec.java
1 /*
2  * Copyright (c) 2020 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.mdsal.binding.dom.codec.osgi.impl;
9
10 import static com.google.common.base.Verify.verify;
11 import static java.util.Objects.requireNonNull;
12
13 import java.util.Collections;
14 import java.util.Comparator;
15 import java.util.IdentityHashMap;
16 import java.util.Map;
17 import java.util.Set;
18 import org.checkerframework.checker.lock.qual.GuardedBy;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.binding.dom.codec.osgi.OSGiBindingDOMCodecServices;
21 import org.opendaylight.mdsal.binding.dom.codec.spi.BindingDOMCodecFactory;
22 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
23 import org.opendaylight.mdsal.binding.runtime.osgi.OSGiBindingRuntimeContext;
24 import org.osgi.service.component.ComponentFactory;
25 import org.osgi.service.component.ComponentInstance;
26 import org.osgi.service.component.annotations.Activate;
27 import org.osgi.service.component.annotations.Component;
28 import org.osgi.service.component.annotations.Deactivate;
29 import org.osgi.service.component.annotations.Reference;
30 import org.osgi.service.component.annotations.ReferenceCardinality;
31 import org.osgi.service.component.annotations.ReferencePolicy;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 @Component(immediate = true)
36 public final class OSGiBindingDOMCodec {
37     // TODO: can we get rid of this complexity?
38     private abstract static class AbstractInstances {
39
40         abstract void add(OSGiBindingRuntimeContext runtimeContext);
41
42         abstract void remove(OSGiBindingRuntimeContext runtimeContext);
43
44         abstract @NonNull AbstractInstances toActive(BindingDOMCodecFactory codecFactory,
45             ComponentFactory<OSGiBindingDOMCodecServices> factory);
46
47         abstract @NonNull AbstractInstances toInactive();
48     }
49
50     private static final class InactiveInstances extends AbstractInstances {
51         private final Set<OSGiBindingRuntimeContext> instances = Collections.newSetFromMap(new IdentityHashMap<>());
52
53         InactiveInstances() {
54
55         }
56
57         InactiveInstances(final Set<OSGiBindingRuntimeContext> keySet) {
58             instances.addAll(keySet);
59         }
60
61         @Override
62         void add(final OSGiBindingRuntimeContext runtimeContext) {
63             verify(instances.add(runtimeContext), "Duplicate instance %s?!", runtimeContext);
64         }
65
66         @Override
67         void remove(final OSGiBindingRuntimeContext runtimeContext) {
68             instances.remove(runtimeContext);
69         }
70
71         @Override
72         AbstractInstances toActive(final BindingDOMCodecFactory codecFactory,
73                 final ComponentFactory<OSGiBindingDOMCodecServices> factory) {
74             final ActiveInstances active = new ActiveInstances(codecFactory, factory);
75             instances.stream()
76                 .sorted(Comparator.comparing(OSGiBindingRuntimeContext::getGeneration).reversed())
77                 .forEach(active::add);
78             return active;
79         }
80
81         @Override
82         AbstractInstances toInactive() {
83             throw new IllegalStateException("Attempted to deactivate inactive instances");
84         }
85     }
86
87     private static final class ActiveInstances extends AbstractInstances {
88         private final Map<OSGiBindingRuntimeContext, ComponentInstance<OSGiBindingDOMCodecServices>> instances =
89             new IdentityHashMap<>();
90         private final ComponentFactory<OSGiBindingDOMCodecServices> factory;
91         private final BindingDOMCodecFactory codecFactory;
92
93         ActiveInstances(final BindingDOMCodecFactory codecFactory,
94                 final ComponentFactory<OSGiBindingDOMCodecServices> factory) {
95             this.codecFactory = requireNonNull(codecFactory);
96             this.factory = requireNonNull(factory);
97         }
98
99         @Override
100         void add(final OSGiBindingRuntimeContext runtimeContext) {
101             final BindingRuntimeContext context = runtimeContext.getService();
102
103             instances.put(runtimeContext, factory.newInstance(OSGiBindingDOMCodecServicesImpl.props(
104                 runtimeContext.getGeneration(), runtimeContext.getServiceRanking(),
105                 codecFactory.createBindingDOMCodec(context))));
106         }
107
108         @Override
109         void remove(final OSGiBindingRuntimeContext runtimeContext) {
110             final ComponentInstance<OSGiBindingDOMCodecServices> instance = instances.remove(runtimeContext);
111             if (instance != null) {
112                 instance.dispose();
113             } else {
114                 LOG.warn("Instance for generation {} not found", runtimeContext.getGeneration());
115             }
116         }
117
118         @Override
119         AbstractInstances toActive(final BindingDOMCodecFactory ignoreCodecFactory,
120                 final ComponentFactory<OSGiBindingDOMCodecServices> ignoreFactory) {
121             throw new IllegalStateException("Attempted to activate active instances");
122         }
123
124         @Override
125         AbstractInstances toInactive() {
126             instances.values().forEach(ComponentInstance::dispose);
127             return new InactiveInstances(instances.keySet());
128         }
129
130     }
131
132     private static final Logger LOG = LoggerFactory.getLogger(OSGiBindingDOMCodec.class);
133
134     @Reference
135     BindingDOMCodecFactory codecFactory = null;
136
137     @Reference(target = "(component.factory=" + OSGiBindingDOMCodecServicesImpl.FACTORY_NAME + ")")
138     ComponentFactory<OSGiBindingDOMCodecServices> contextFactory = null;
139
140     @GuardedBy("this")
141     private AbstractInstances instances = new InactiveInstances();
142
143     @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
144     synchronized void addModuleInfoSnapshot(final OSGiBindingRuntimeContext runtimeContext) {
145         instances.add(runtimeContext);
146     }
147
148     synchronized void removeModuleInfoSnapshot(final OSGiBindingRuntimeContext runtimeContext) {
149         instances.remove(runtimeContext);
150     }
151
152     @Activate
153     synchronized void activate() {
154         LOG.info("Binding/DOM Codec activating");
155         instances = instances.toActive(codecFactory, contextFactory);
156         LOG.info("Binding/DOM Codec activated");
157     }
158
159     @Deactivate
160     synchronized void deactivate() {
161         LOG.info("Binding/DOM Codec deactivating");
162         instances = instances.toInactive();
163         LOG.info("Binding/DOM Codec deactivated");
164     }
165 }