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