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