1 package com.atlassian.plugin.osgi.hostcomponents.impl;
2
3 import com.atlassian.plugin.hostcontainer.HostContainer;
4 import com.atlassian.plugin.osgi.hostcomponents.ComponentRegistrar;
5 import com.atlassian.plugin.osgi.hostcomponents.ContextClassLoaderStrategy;
6 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
7 import com.atlassian.plugin.osgi.hostcomponents.InstanceBuilder;
8 import com.atlassian.plugin.osgi.hostcomponents.PropertyBuilder;
9 import com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler;
10 import com.google.common.collect.Maps;
11 import org.osgi.framework.Bundle;
12 import org.osgi.framework.BundleContext;
13 import org.osgi.framework.ServiceFactory;
14 import org.osgi.framework.ServiceRegistration;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Proxy;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.concurrent.CopyOnWriteArrayList;
27 import java.util.function.Function;
28
29
30
31
32 public class DefaultComponentRegistrar implements ComponentRegistrar {
33 private static final Logger log = LoggerFactory.getLogger(DefaultComponentRegistrar.class);
34
35 private final List<HostComponentRegistration> registry = new CopyOnWriteArrayList<>();
36
37 public InstanceBuilder register(final Class<?>... mainInterfaces) {
38 final Registration reg = new Registration(mainInterfaces);
39 registry.add(reg);
40 return new DefaultInstanceBuilder(reg);
41 }
42
43 public List<ServiceRegistration> writeRegistry(final BundleContext ctx) {
44 final ArrayList<ServiceRegistration> services = new ArrayList<>();
45
46 for (final HostComponentRegistration reg : new ArrayList<>(registry)) {
47 if (Arrays.asList(reg.getMainInterfaceClasses()).contains(HostContainer.class)) {
48 log.warn("Cannot register a HostContainer as a host component, skipping");
49 registry.remove(reg);
50 continue;
51 }
52
53 final String[] names = reg.getMainInterfaces();
54
55 reg.getProperties().put(HOST_COMPONENT_FLAG, Boolean.TRUE.toString());
56
57
58 final String beanName = reg.getProperties().get(PropertyBuilder.BEAN_NAME);
59 if (beanName == null) {
60 String genKey = String.valueOf(Arrays.asList(reg.getMainInterfaces()).hashCode());
61 reg.getProperties().put(PropertyBuilder.BEAN_NAME, "hostComponent-" + genKey);
62 }
63
64 if (log.isDebugEnabled()) {
65 log.debug("Registering: " + Arrays.asList(names) + " instance " + reg.getInstance() + "with properties: " + reg.getProperties());
66 }
67
68 if (names.length == 0) {
69 log.warn("Host component " + beanName + " of instance " + reg.getInstance() + " has no interfaces");
70 }
71
72 Object service = reg.getInstance();
73
74 if (!ContextClassLoaderStrategy.USE_PLUGIN.name().equals(reg.getProperties().get(PropertyBuilder.CONTEXT_CLASS_LOADER_STRATEGY))) {
75 service = createContextClassLoaderSettingProxy(reg.getMainInterfaceClasses(), service);
76 }
77
78 if (Boolean.parseBoolean(reg.getProperties().get(PropertyBuilder.TRACK_BUNDLE))) {
79 service = createTrackBundleProxy(reg.getMainInterfaceClasses(), service);
80 }
81
82 final ServiceRegistration sreg = ctx.registerService(names, service, reg.getProperties());
83 if (sreg != null) {
84 services.add(sreg);
85 }
86 }
87 return Collections.unmodifiableList(services);
88 }
89
90 public List<HostComponentRegistration> getRegistry() {
91 return Collections.unmodifiableList(registry);
92 }
93
94
95
96
97
98
99
100
101
102 private Object createContextClassLoaderSettingProxy(final Class<?>[] interfaces, final Object service) {
103 final Function<Object, Object> transformer = service1 ->
104 Proxy.newProxyInstance(DefaultComponentRegistrar.class.getClassLoader(), interfaces, new ContextClassLoaderSettingInvocationHandler(service1));
105
106 if (!(service instanceof ServiceFactory)) {
107 return transformer.apply(service);
108 }
109 return new TransformingServiceFactory((ServiceFactory) service) {
110 @Override
111 protected Object transform(Bundle bundle, ServiceRegistration registration, Object service) {
112 return transformer.apply(service);
113 }
114 };
115 }
116
117 private ServiceFactory createTrackBundleProxy(final Class<?>[] interfaces, final Object service) {
118 final ServiceFactory delegate = service instanceof ServiceFactory ?
119 (ServiceFactory) service :
120 new InstanceServiceFactory(service);
121
122 return new TransformingServiceFactory(delegate) {
123 @Override
124 protected Object transform(final Bundle bundle, final ServiceRegistration registration, final Object service) {
125 return Proxy.newProxyInstance(DefaultComponentRegistrar.class.getClassLoader(), interfaces,
126 new BundleTrackingInvocationHandler(bundle, service));
127 }
128 };
129 }
130
131 private static class BundleTrackingInvocationHandler implements InvocationHandler {
132
133 private final Bundle bundle;
134 private final Object service;
135
136 private BundleTrackingInvocationHandler(Bundle bundle, Object service) {
137 this.bundle = bundle;
138 this.service = service;
139 }
140
141 @Override
142 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
143 Bundle original = CallingBundleStore.get();
144 try {
145 CallingBundleStore.set(bundle);
146 return method.invoke(service, args);
147 } finally {
148 CallingBundleStore.set(original);
149 }
150 }
151 }
152
153 private static class InstanceServiceFactory implements ServiceFactory {
154
155 private final Object service;
156
157 private InstanceServiceFactory(final Object service) {
158 this.service = service;
159 }
160
161 @Override
162 public Object getService(final Bundle bundle, final ServiceRegistration registration) {
163 return service;
164 }
165
166 @Override
167 public void ungetService(final Bundle bundle, final ServiceRegistration registration, final Object service) {
168
169 }
170 }
171
172 private static abstract class TransformingServiceFactory implements ServiceFactory {
173
174 private final ServiceFactory delegate;
175 private final Map<Long, Object> bundleIdToOriginalService;
176
177 private TransformingServiceFactory(ServiceFactory delegate) {
178 this.delegate = delegate;
179 this.bundleIdToOriginalService = Maps.newConcurrentMap();
180 }
181
182
183 @Override
184 public final Object getService(final Bundle bundle, final ServiceRegistration registration) {
185 final Object service = delegate.getService(bundle, registration);
186 final Object transformed = transform(bundle, registration, service);
187
188
189 bundleIdToOriginalService.put(bundle.getBundleId(), service);
190 return transformed;
191 }
192
193 @Override
194 public final void ungetService(final Bundle bundle, final ServiceRegistration registration, final Object transformed) {
195 final Object service = bundleIdToOriginalService.remove(bundle.getBundleId());
196 if (service != null) {
197 delegate.ungetService(bundle, registration, service);
198 }
199 }
200
201 protected abstract Object transform(Bundle bundle, ServiceRegistration registration, Object service);
202
203 }
204
205 }