blob: f1bde97dfe7a322248828cb5421192b1af64df1e [file] [log] [blame]
Pierre De Rop3a00a212015-03-01 09:27:46 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19package org.apache.felix.dm.runtime;
20
21import java.io.BufferedReader;
22import java.io.IOException;
23import java.io.InputStreamReader;
24import java.net.URL;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.List;
28import java.util.Map;
29
30import org.apache.felix.dm.Component;
31import org.apache.felix.dm.DependencyManager;
32import org.osgi.framework.Bundle;
33import org.osgi.service.packageadmin.PackageAdmin;
34
35/**
36 * This class parses service descriptors generated by the annotation bnd processor.
37 * The descriptors are located under META-INF/dependencymanager directory. Such files are actually
38 * referenced by a specific "DependendencyManager-Component" manifest header.
39 *
40 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
41 */
42public class DependencyManagerRuntime
43{
44 /**
45 * Map between bundles and their corresponding DependencyManager objects used to create bundle's components.
46 * Notice that we can safely use this map without synchronization because we are relying on the new DM4 thread
47 * model which serialize all component events safely.
48 */
49 private final Map<Bundle, DependencyManager> m_managers = new HashMap<Bundle, DependencyManager>();
50
51 /**
52 * Parser used to scan component descriptors defined in bundles meta data.
53 */
54 private final DescriptorParser m_parser;
55
56 /**
57 * We use the PackageAdmin service to allow support for annotations in fragment bundles.
58 */
59 private volatile PackageAdmin m_packageAdmin;
60
61 /**
62 * Our constructor. We'll initialize here our DM component builders.
63 */
64 public DependencyManagerRuntime()
65 {
66 // Instantiates our descriptor parser, and register our service builders into it.
67 m_parser = new DescriptorParser();
68 m_parser.addBuilder(new ComponentBuilder());
69 m_parser.addBuilder(new AspectServiceBuilder());
70 m_parser.addBuilder(new AdapterServiceBuilder());
71 m_parser.addBuilder(new BundleAdapterServiceBuilder());
72 m_parser.addBuilder(new FactoryConfigurationAdapterServiceBuilder());
73 m_parser.addBuilder(new ResourceAdapterServiceBuilder());
74 }
75
76 /**
77 * Return our Object Composition (the Activator will inject dependencies into it)
78 */
79 protected Object[] getComposition()
80 {
81 return new Object[] { this, Log.instance() };
82 }
83
84 /**
85 * Starts our Service (at this point, we have been injected with our bundle context, as well
86 * as with our log service. We'll listen to bundle start/stop events (we implement the
87 * SynchronousBundleListener interface).
88 */
89 protected void start()
90 {
Pierre De Rop9c082ef2016-02-09 21:21:39 +000091 Log.instance().info("Starting Dependency Manager annotation runtime.");
Pierre De Rop3a00a212015-03-01 09:27:46 +000092 }
93
94 /**
95 * Stops our service. We'll stop all activated DependencyManager services.
96 */
97 protected void stop()
98 {
99 Log.instance().info("Runtime: stopping services");
100 for (DependencyManager dm : m_managers.values())
101 {
102 List<Component> services = new ArrayList<Component>(dm.getComponents());
103 for (Component service : services)
104 {
105 dm.remove(service);
106 }
107 }
108
109 m_managers.clear();
110 }
111
112 /**
113 * Load the DM descriptors from the started bundle. We also check possible fragments
114 * attached to the bundle, which might also contain some DM descriptors.
115 * @param bundle the started bundle which contains a DependencyManager-Component header
116 */
117 protected void bundleStarted(Bundle bundle)
118 {
119 Log.instance().info("Scanning started bundle %s", bundle.getSymbolicName());
120 List<URL> descriptorURLs = new ArrayList<URL>();
121 collectDescriptors(bundle, descriptorURLs);
122 Bundle[] fragments = m_packageAdmin.getFragments(bundle);
123 if (fragments != null)
124 {
125 for (Bundle fragment : fragments)
126 {
127 collectDescriptors(fragment, descriptorURLs);
128 }
129 }
130 for (URL descriptorURL : descriptorURLs)
131 {
132 loadDescriptor(bundle, descriptorURL);
133 }
134 }
135
136 /**
137 * Unregisters all services for a stopping bundle.
138 * @param b
139 */
140 protected void bundleStopped(Bundle b)
141 {
142 Log.instance().info("Runtime: Removing services from stopping bundle: %s", b.getSymbolicName());
143 DependencyManager dm = m_managers.remove(b);
144 if (dm != null)
145 {
146 List<Component> services = new ArrayList<Component>(dm.getComponents());
147 for (Component service : services)
148 {
149 Log.instance().info("Runtime: Removing service: %s", service);
150 dm.remove(service);
151 }
152 }
153 }
154
155 /**
156 * Collect all descriptors found from a given bundle, including its possible attached fragments.
157 * @param bundle a started bundle containing some DM descriptors
158 * @param out the list of descriptors' URLS found from the started bundle, as well as from possibly
159 * attached fragments.
160 */
161 private void collectDescriptors(Bundle bundle, List<URL> out) {
162 String descriptorPaths = (String) bundle.getHeaders().get("DependencyManager-Component");
163 if (descriptorPaths == null)
164 {
165 return;
166 }
167
168 for (String descriptorPath : descriptorPaths.split(","))
169 {
170 URL descriptorURL = bundle.getEntry(descriptorPath);
171 if (descriptorURL == null)
172 {
173 Log.instance()
174 .error("Runtime: " + "DependencyManager component descriptor not found: %s",
175 descriptorPath);
176 continue;
177 }
178 out.add(descriptorURL);
179 }
180 }
181
182 /**
183 * Load a DependencyManager component descriptor from a given bundle.
184 * @param b
185 * @param descriptorURL
186 */
187 private void loadDescriptor(Bundle b, URL descriptorURL)
188 {
189 Log.instance().debug("Parsing descriptor %s from bundle %s", descriptorURL, b.getSymbolicName());
190
191 BufferedReader in = null;
192 try
193 {
194 in = new BufferedReader(new InputStreamReader(descriptorURL.openStream()));
195 DependencyManager dm = m_managers.get(b);
196 if (dm == null)
197 {
198 dm = new DependencyManager(b.getBundleContext());
199 m_managers.put(b, dm);
200 }
201
202 m_parser.parse(in, b, dm);
203 }
204
205 catch (Throwable t)
206 {
207 Log.instance().error("Runtime: Error while parsing descriptor %s from bundle %s",
208 t,
209 descriptorURL,
210 b.getSymbolicName());
211 }
212
213 finally
214 {
215 if (in != null)
216 {
217 try
218 {
219 in.close();
220 }
221 catch (IOException ignored)
222 {
223 }
224 }
225 }
226 }
227}