/*******************************************************************************
* Copyright (c) 2012 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.kernel.osgi.framework;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link ServiceUtils} is a collection of OSGi service utilities for use by the kernel.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
* Thread safe
*/
public class ServiceUtils {
public static final String PROPERTY_KERNEL_STARTUP_WAIT_LIMIT = "org.eclipse.virgo.kernel.startup.wait.limit";
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceUtils.class);
private static final int DEFAULT_STARTUP_WAIT_LIMIT = 180; // 3 minutes
private static volatile int maxSecondsWaitForService = DEFAULT_STARTUP_WAIT_LIMIT;
private static volatile long maxMillisWaitForService = 0;
private static final Object monitor = new Object();
/**
* Wait for a service of the given class but throw a TimeoutException if this takes longer than the number of
* seconds configured in the framework property org.eclipse.virgo.kernel.startup.wait.limit.
*/
public static <T> T getPotentiallyDelayedService(BundleContext context, Class<T> serviceClass) throws TimeoutException, InterruptedException {
T service = null;
OsgiServiceHolder<T> serviceHolder;
long millisWaited = 0;
while (service == null && millisWaited <= getInitialisedWaitLimit(context)) {
try {
serviceHolder = OsgiFrameworkUtils.getService(context, serviceClass);
if (serviceHolder != null) {
service = serviceHolder.getService();
} else {
millisWaited += sleepABitMore();
}
} catch (IllegalStateException e) {
}
}
if (service == null) {
throw new TimeoutException(serviceClass.getName());
}
return service;
}
private static long getInitialisedWaitLimit(BundleContext context) {
if (maxMillisWaitForService == 0) {
synchronized (monitor) {
if (maxMillisWaitForService == 0) {
maxSecondsWaitForService = readBundleStartupWaitLimit(context);
maxMillisWaitForService = TimeUnit.SECONDS.toMillis(maxSecondsWaitForService);
}
}
}
return maxMillisWaitForService;
}
/**
* Returns the service wait limit in seconds. This method will only return the correct value after
* getInitialisedWaitLimit has been called, for instance after TimeoutException has been thrown from
* getPotentiallyDelayedService.
*
* @return the service wait limit in seconds or 0 if this has not been initialised
*/
public static long getWaitLimitSeconds() {
return maxSecondsWaitForService;
}
private static int readBundleStartupWaitLimit(BundleContext context) {
String waitLimitProperty = readFrameworkProperty(PROPERTY_KERNEL_STARTUP_WAIT_LIMIT, context);
if (!hasText(waitLimitProperty)) {
return DEFAULT_STARTUP_WAIT_LIMIT;
}
try {
return Integer.parseInt(waitLimitProperty);
} catch (NumberFormatException e) {
LOGGER.warn("Could not parse property {} with value '{}'. Using default limit {} seconds", new Object[] {
PROPERTY_KERNEL_STARTUP_WAIT_LIMIT, waitLimitProperty, DEFAULT_STARTUP_WAIT_LIMIT });
return DEFAULT_STARTUP_WAIT_LIMIT;
}
}
private static String readFrameworkProperty(String propertyKey, BundleContext context) {
return context.getProperty(propertyKey);
}
private static boolean hasText(String string) {
return (string != null && !string.trim().isEmpty());
}
private static long sleepABitMore() throws InterruptedException {
long before = System.currentTimeMillis();
Thread.sleep(100);
return System.currentTimeMillis() - before;
}
}