blob: db4a611ddeedd154160d5fcea182bca5180a1670 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.framework;
import java.util.ArrayList;
import java.util.List;
import org.osgi.framework.AdminPermission;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.service.startlevel.StartLevel;
class FrameworkStartLevelImpl implements FrameworkStartLevel, Runnable
{
static final String THREAD_NAME = "FelixStartLevel";
private static final int BUNDLE_IDX = 0;
private static final int STARTLEVEL_IDX = 1;
private final Felix m_felix;
private final ServiceRegistry m_registry;
private final List m_requests = new ArrayList();
private final List<FrameworkListener[]> m_requestListeners
= new ArrayList<FrameworkListener[]>();
private ServiceRegistration<StartLevel> m_slReg;
private Thread m_thread = null;
FrameworkStartLevelImpl(Felix felix, ServiceRegistry registry)
{
m_felix = felix;
m_registry = registry;
}
@SuppressWarnings("unchecked")
void start()
{
m_slReg = (ServiceRegistration<StartLevel>) m_registry.registerService(m_felix,
new String[] { StartLevel.class.getName() },
new StartLevelImpl(m_felix),
null);
}
// Should only be called hold requestList lock.
private void startThread()
{
// Start a thread to perform asynchronous package refreshes.
if (m_thread == null)
{
m_thread = new Thread(this, THREAD_NAME);
m_thread.setDaemon(true);
m_thread.start();
}
}
/**
* Stops the FelixStartLevel thread on system shutdown. Shutting down the
* thread explicitly is required in the embedded case, where Felix may be
* stopped without the Java VM being stopped. In this case the
* FelixStartLevel thread must be stopped explicitly.
* <p>
* This method is called by the
* {@link StartLevelActivator#stop(BundleContext)} method.
*/
void stop()
{
synchronized (m_requests)
{
if (m_thread != null)
{
// Null thread variable to signal to the thread that
// we want it to exit.
m_thread = null;
// Wake up the thread, if it is currently in the wait() state
// for more work.
m_requests.notifyAll();
}
}
}
public Bundle getBundle()
{
return m_felix;
}
public int getStartLevel()
{
return m_felix.getActiveStartLevel();
}
public void setStartLevel(int startlevel, FrameworkListener... listeners)
{
Object sm = System.getSecurityManager();
if (sm != null)
{
((SecurityManager) sm).checkPermission(
new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
}
if (startlevel <= 0)
{
throw new IllegalArgumentException(
"Start level must be greater than zero.");
}
synchronized (m_requests)
{
if (m_thread == null)
{
throw new IllegalStateException("No inital startlevel yet");
}
// Queue request.
m_requestListeners.add(listeners);
m_requests.add(new Integer(startlevel));
m_requests.notifyAll();
}
}
/**
* This method is currently only called by the by the thread that calls
* the Felix.start() method and the shutdown thread when the
* framework is shutting down.
* @param startlevel
**/
/* package */ void setStartLevelAndWait(int startlevel)
{
Object request = new Integer(startlevel);
synchronized (request)
{
synchronized (m_requests)
{
// Start thread if necessary.
startThread();
// Queue request.
m_requestListeners.add(null);
m_requests.add(request);
m_requests.notifyAll();
}
try
{
request.wait();
}
catch (InterruptedException ex)
{
// Log it and ignore since it won't cause much of an issue.
m_felix.getLogger().log(
Logger.LOG_WARNING,
"Wait for start level change during shutdown interrupted.",
ex);
}
}
}
public int getInitialBundleStartLevel()
{
return m_felix.getInitialBundleStartLevel();
}
public void setInitialBundleStartLevel(int startlevel)
{
Object sm = System.getSecurityManager();
if (sm != null)
{
((SecurityManager) sm).checkPermission(
new AdminPermission(m_felix, AdminPermission.STARTLEVEL));
}
m_felix.setInitialBundleStartLevel(startlevel);
}
BundleStartLevel createBundleStartLevel(BundleImpl bundle)
{
return new BundleStartLevelImpl(bundle);
}
class BundleStartLevelImpl implements BundleStartLevel
{
private BundleImpl m_bundle;
private BundleStartLevelImpl(BundleImpl bundle)
{
m_bundle = bundle;
}
public Bundle getBundle()
{
return m_bundle;
}
public int getStartLevel()
{
return m_felix.getBundleStartLevel(m_bundle);
}
public void setStartLevel(int startlevel)
{
Object sm = System.getSecurityManager();
if (sm != null)
{
((SecurityManager) sm).checkPermission(
new AdminPermission(m_bundle, AdminPermission.EXECUTE));
}
if (m_bundle.getBundleId() == 0)
{
throw new IllegalArgumentException(
"Cannot change system bundle start level.");
}
else if (startlevel <= 0)
{
throw new IllegalArgumentException(
"Start level must be greater than zero.");
}
synchronized (m_requests)
{
// Start thread if necessary.
startThread();
// Synchronously persists the start level.
m_bundle.setStartLevel(startlevel);
// Queue request.
m_requestListeners.add(null);
m_requests.add(new Object[] { m_bundle, new Integer(startlevel) });
m_requests.notifyAll();
}
}
public boolean isPersistentlyStarted()
{
return m_felix.isBundlePersistentlyStarted(m_bundle);
}
public boolean isActivationPolicyUsed()
{
return m_felix.isBundleActivationPolicyUsed(m_bundle);
}
}
public void run()
{
// This thread loops forever, thus it should
// be a daemon thread.
Object previousRequest = null;
while (true)
{
Object request = null;
FrameworkListener[] listeners = null;
synchronized (m_requests)
{
// Wait for a request.
while (m_requests.isEmpty())
{
// Terminate the thread if requested to do so (see stop()).
if (m_thread == null)
{
return;
}
try
{
m_requests.wait();
}
catch (InterruptedException ex)
{
// Ignore.
}
}
// Get the requested start level.
request = m_requests.remove(0);
listeners = m_requestListeners.remove(0);
}
// If the request object is an Integer, then the request
// is to set the framework start level. If the request is
// an Object array, then the request is to set the start
// level for a bundle.
// NOTE: We don't catch any exceptions here, because
// the invoked methods shield us from exceptions by
// catching Throwables when they invoke callbacks.
if (request instanceof Integer)
{
// Set the new framework start level.
try
{
m_felix.setActiveStartLevel(((Integer) request).intValue(), listeners);
}
catch (IllegalStateException ise)
{
// Thrown if global lock cannot be acquired, in which case
// just retry (unless we already did)
if (previousRequest == request)
{
m_felix.getLogger().log(Logger.LOG_ERROR,
"Unexpected problem setting active start level to " + request, ise);
}
else
{
synchronized (m_requests)
{
m_requests.add(0, request);
previousRequest = request;
}
}
}
catch (Exception ex)
{
m_felix.getLogger().log(Logger.LOG_ERROR,
"Unexpected problem setting active start level to " + request, ex);
}
}
else
{
Bundle bundle = (Bundle) ((Object[]) request)[BUNDLE_IDX];
int startlevel = ((Integer) ((Object[]) request)[STARTLEVEL_IDX]).intValue();
m_felix.setBundleStartLevel(bundle, startlevel);
}
// Notify any waiting thread that this request is done.
synchronized (request)
{
request.notifyAll();
}
}
}
}