blob: e7365ccdf342bb0501d406fe740715a133e1790f [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.impl;
20
21import java.util.concurrent.ConcurrentHashMap;
22import java.util.concurrent.ConcurrentMap;
23import java.util.concurrent.Executor;
24
25import org.apache.felix.dm.Component;
26import org.apache.felix.dm.ComponentDeclaration;
27import org.apache.felix.dm.ComponentExecutorFactory;
28import org.apache.felix.dm.context.ComponentContext;
29import org.osgi.framework.BundleContext;
30import org.osgi.service.component.ComponentFactory;
31
32/**
33 * The Dependency Manager delegates all components addition/removal to this class.
34 * If a ComponentExecutorFactory is registered in the OSGi registry, this class will use it to get an
35 * Executor used for components management and lifecycle callbacks.
36 *
37 * @see {@link ComponentFactory}
38 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
39 */
40public class ComponentScheduler {
41 private final static ComponentScheduler m_instance = new ComponentScheduler();
42 private final static String PARALLEL = "org.apache.felix.dependencymanager.parallel";
43 private volatile ComponentExecutorFactory m_componentExecutorFactory;
44 private final Executor m_serial = new SerialExecutor(null);
45 private ConcurrentMap<Component, Component> m_pending = new ConcurrentHashMap<>();
46
47 public static ComponentScheduler instance() {
48 return m_instance;
49 }
50
51 protected void bind(final ComponentExecutorFactory componentExecutorFactory) {
52 m_componentExecutorFactory = componentExecutorFactory;
53 m_serial.execute(new Runnable() {
54 @Override
55 public void run() {
56 for (Component c : m_pending.keySet()) {
57 createComponentExecutor(m_componentExecutorFactory, c);
58 ((ComponentContext) c).start();
59 }
60 m_pending.clear();
61 }
62 });
63 }
64
65 protected void unbind(ComponentExecutorFactory threadPool) {
66 m_componentExecutorFactory = null;
67 }
68
69 public void add(final Component c) {
70 if (mayStartNow(c)) {
71 ((ComponentContext) c).start();
72 }
73 else {
74 // The component requires a threadpool: delay execution until one is available.
75 m_serial.execute(new Runnable() {
76 @Override
77 public void run() {
78 ComponentExecutorFactory execFactory = m_componentExecutorFactory;
79 if (execFactory == null) {
80 m_pending.put(c, c);
81 }
82 else {
83 createComponentExecutor(execFactory, c);
84 ((ComponentContext) c).start();
85 }
86 }
87 });
88 }
89 }
90
91 public void remove(final Component c) {
92 m_pending.remove(c);
93 ((ComponentContext) c).stop();
94 }
95
96 private boolean mayStartNow(Component c) {
97 ComponentExecutorFactory execFactory = m_componentExecutorFactory;
98 BundleContext ctx = c.getDependencyManager().getBundleContext();
99 String parallel = ctx.getProperty(PARALLEL);
100
101 if (execFactory == null) {
102 // No ComponentExecutorFactory available. If a "parallel" OSGi system property is specified,
103 // we have to wait for a ComponentExecutorFactory servoce if the component class name is matching one of the
104 // prefixes specified in the "parallel" system property.
105 if (parallel != null && requiresThreadPool(c, parallel)) {
106 return false; // wait for a threadpool
107 } else {
108 return true; // no threadpool required, start the component now, synchronously
109 }
110 }
111 else {
112 // A threadpool is there. If the "parallel" OSGi system property is not specified, we can start the component
113 // now and we'll use the threadpool for it.
114 // But if the "parallel" system property is specified, the component will use the threadpool only if it's
115 // classname is starting with one of the prefixes specified in the property.
116 if (parallel == null || requiresThreadPool(c, parallel)) {
Pierre De Ropc8295c22015-06-04 10:15:35 +0000117 createComponentExecutor(execFactory, c);
Pierre De Rop3a00a212015-03-01 09:27:46 +0000118 }
119 return true; // start the component now, possibly using the threadpool (see above).
120 }
121 }
122
123 private boolean requiresThreadPool(Component c, String parallel) {
124 // The component declared from our DM Activator can not be parallel.
125 ComponentDeclaration decl = c.getComponentDeclaration();
126 if (ComponentScheduler.class.getName().equals(decl.getName())) {
127 return false;
128 }
129
130 for (String prefix : parallel.trim().split(",")) {
131 prefix = prefix.trim();
132 boolean not = prefix.startsWith("!");
133 if (not) {
134 prefix = prefix.substring(1).trim();
135 }
136 if ("*".equals(prefix) || c.getComponentDeclaration().getClassName().startsWith(prefix)) {
137 return !not;
138 }
139 }
140 return false;
141 }
142
143 private void createComponentExecutor(ComponentExecutorFactory execFactory, Component c) {
144 Executor exec = execFactory.getExecutorFor(c);
145 if (exec != null) {
146 ((ComponentContext) c).setThreadPool(exec);
147 }
148 }
149}