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 }