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