001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.felix.framework;
020
021 import java.util.ArrayList;
022 import java.util.List;
023
024 import org.osgi.framework.AdminPermission;
025 import org.osgi.framework.Bundle;
026 import org.osgi.service.startlevel.StartLevel;
027
028 /**
029 * StartLevel service implementation.
030 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
031 **/
032 public class StartLevelImpl implements StartLevel, Runnable
033 {
034 private static final int BUNDLE_IDX = 0;
035 private static final int STARTLEVEL_IDX = 1;
036
037 private final Felix m_felix;
038 private final List m_requestList = new ArrayList();
039 private Thread m_thread = null;
040
041 public StartLevelImpl(Felix felix)
042 {
043 m_felix = felix;
044 // Start a thread to perform asynchronous package refreshes.
045 m_thread = new Thread(this, "FelixStartLevel");
046 m_thread.setDaemon(true);
047 m_thread.start();
048 }
049
050 /**
051 * Stops the FelixStartLevel thread on system shutdown. Shutting down the
052 * thread explicitly is required in the embedded case, where Felix may be
053 * stopped without the Java VM being stopped. In this case the
054 * FelixStartLevel thread must be stopped explicitly.
055 * <p>
056 * This method is called by the
057 * {@link StartLevelActivator#stop(BundleContext)} method.
058 */
059 void stop()
060 {
061 synchronized (m_requestList)
062 {
063 if (m_thread != null)
064 {
065 // Null thread variable to signal to the thread that
066 // we want it to exit.
067 m_thread = null;
068
069 // Wake up the thread, if it is currently in the wait() state
070 // for more work.
071 m_requestList.notifyAll();
072 }
073 }
074 }
075
076 /* (non-Javadoc)
077 * @see org.osgi.service.startlevel.StartLevel#getStartLevel()
078 **/
079 public int getStartLevel()
080 {
081 return m_felix.getActiveStartLevel();
082 }
083
084 /* (non-Javadoc)
085 * @see org.osgi.service.startlevel.StartLevel#setStartLevel(int)
086 **/
087 public void setStartLevel(int startlevel)
088 {
089 Object sm = System.getSecurityManager();
090
091 if (sm != null)
092 {
093 ((SecurityManager) sm).checkPermission(
094 new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
095 }
096
097 if (startlevel <= 0)
098 {
099 throw new IllegalArgumentException(
100 "Start level must be greater than zero.");
101 }
102
103 synchronized (m_requestList)
104 {
105 m_requestList.add(new Integer(startlevel));
106 m_requestList.notifyAll();
107 }
108 }
109
110 /**
111 * This method is currently only called by the by the thread that calls
112 * the Felix.start() method and the shutdown thread when the
113 * framework is shutting down.
114 * @param startlevel
115 **/
116 /* package */ void setStartLevelAndWait(int startlevel)
117 {
118 Object request = new Integer(startlevel);
119 synchronized (request)
120 {
121 synchronized (m_requestList)
122 {
123 m_requestList.add(request);
124 m_requestList.notifyAll();
125 }
126
127 try
128 {
129 request.wait();
130 }
131 catch (InterruptedException ex)
132 {
133 // Log it and ignore since it won't cause much of an issue.
134 m_felix.getLogger().log(
135 Logger.LOG_WARNING,
136 "Wait for start level change during shutdown interrupted.",
137 ex);
138 }
139 }
140 }
141
142 /* (non-Javadoc)
143 * @see org.osgi.service.startlevel.StartLevel#getBundleStartLevel(org.osgi.framework.Bundle)
144 **/
145 public int getBundleStartLevel(Bundle bundle)
146 {
147 return m_felix.getBundleStartLevel(bundle);
148 }
149
150 /* (non-Javadoc)
151 * @see org.osgi.service.startlevel.StartLevel#setBundleStartLevel(org.osgi.framework.Bundle, int)
152 **/
153 public void setBundleStartLevel(Bundle bundle, int startlevel)
154 {
155 Object sm = System.getSecurityManager();
156
157 if (sm != null)
158 {
159 ((SecurityManager) sm).checkPermission(
160 new AdminPermission(bundle, AdminPermission.EXECUTE));
161 }
162
163 if (bundle.getBundleId() == 0)
164 {
165 throw new IllegalArgumentException(
166 "Cannot change system bundle start level.");
167 }
168 else if (startlevel <= 0)
169 {
170 throw new IllegalArgumentException(
171 "Start level must be greater than zero.");
172 }
173 synchronized (m_requestList)
174 {
175 // Synchronously persists the start level.
176 ((BundleImpl) bundle).setStartLevel(startlevel);
177 // Asynchronously process the start level change.
178 m_requestList.add(new Object[] { bundle, new Integer(startlevel) });
179 m_requestList.notifyAll();
180 }
181 }
182
183 /* (non-Javadoc)
184 * @see org.osgi.service.startlevel.StartLevel#getInitialBundleStartLevel()
185 **/
186 public int getInitialBundleStartLevel()
187 {
188 return m_felix.getInitialBundleStartLevel();
189 }
190
191 /* (non-Javadoc)
192 * @see org.osgi.service.startlevel.StartLevel#setInitialBundleStartLevel(int)
193 **/
194 public void setInitialBundleStartLevel(int startlevel)
195 {
196 Object sm = System.getSecurityManager();
197
198 if (sm != null)
199 {
200 ((SecurityManager) sm).checkPermission(
201 new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
202 }
203 m_felix.setInitialBundleStartLevel(startlevel);
204 }
205
206 /* (non-Javadoc)
207 * @see org.osgi.service.startlevel.StartLevel#isBundlePersistentlyStarted(org.osgi.framework.Bundle)
208 **/
209 public boolean isBundlePersistentlyStarted(Bundle bundle)
210 {
211 return m_felix.isBundlePersistentlyStarted(bundle);
212 }
213
214 /* (non-Javadoc)
215 * @see org.osgi.service.startlevel.StartLevel#isBundleActivationPolicyUsed(org.osgi.framework.Bundle)
216 **/
217 public boolean isBundleActivationPolicyUsed(Bundle bundle)
218 {
219 return m_felix.isBundleActivationPolicyUsed(bundle);
220 }
221
222 public void run()
223 {
224 // This thread loops forever, thus it should
225 // be a daemon thread.
226 while (true)
227 {
228 Object request = null;
229 synchronized (m_requestList)
230 {
231 // Wait for a request.
232 while (m_requestList.size() == 0)
233 {
234 // Terminate the thread if requested to do so (see stop()).
235 if (m_thread == null)
236 {
237 return;
238 }
239
240 try
241 {
242 m_requestList.wait();
243 }
244 catch (InterruptedException ex)
245 {
246 // Ignore.
247 }
248 }
249
250 // Get the requested start level.
251 request = m_requestList.remove(0);
252 }
253
254 // If the request object is an Integer, then the request
255 // is to set the framework start level. If the request is
256 // an Object array, then the request is to set the start
257 // level for a bundle.
258 // NOTE: We don't catch any exceptions here, because
259 // the invoked methods shield us from exceptions by
260 // catching Throwables when they invoke callbacks.
261 if (request instanceof Integer)
262 {
263 // Set the new framework start level.
264 m_felix.setActiveStartLevel(((Integer) request).intValue());
265 }
266 else
267 {
268 Bundle bundle = (Bundle) ((Object[]) request)[BUNDLE_IDX];
269 int startlevel = ((Integer) ((Object[]) request)[STARTLEVEL_IDX]).intValue();
270 m_felix.setBundleStartLevel(bundle, startlevel);
271 }
272
273 // Notify any waiting thread that this request is done.
274 synchronized (request)
275 {
276 request.notifyAll();
277 }
278 }
279 }
280 }