/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.communication.rpc.internal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import de.rcenvironment.core.communication.rpc.spi.LocalServiceResolver;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.rpc.RemoteOperationException;
/**
* Resolves service requests against the OSGi service registry, using the containing bundle's {@link BundleContext}.
*
* @author Robert Mischke
*/
public class OSGiLocalServiceResolver implements LocalServiceResolver {
private static final int SINGLETON_SERVICE_RESOLUTION_NUM_ATTEMPTS = 10;
private static final int SERVICE_RESOLUTION_RETRY_DELAY_MSEC = 1000;
private static final String ERROR_GET_SERVICE = "Failed to acquire OSGi service ";
private final Log log = LogFactory.getLog(getClass());
private BundleContext bundleContext;
private final int retryCount;
/**
* Default constructor; uses a retry count of {@link #SINGLETON_SERVICE_RESOLUTION_NUM_ATTEMPTS}.
*/
public OSGiLocalServiceResolver() {
this.retryCount = SINGLETON_SERVICE_RESOLUTION_NUM_ATTEMPTS;
}
/**
* Constructor that allows setting a custom retry count (intended to speed up unit testing).
*
* @param retryCount the number of lookup retries before giving up
*/
public OSGiLocalServiceResolver(int retryCount) {
this.retryCount = retryCount;
}
/**
* OSGi life cycle method.
*
* @param context the {@link BundleContext} to resolve against
*/
public void activate(BundleContext context) {
this.bundleContext = context;
}
@Override
public Object getLocalService(String serviceName) {
// allow several retries as the service may be still starting up
return getLocalSingletonService(serviceName, retryCount, SERVICE_RESOLUTION_RETRY_DELAY_MSEC);
}
/**
*
* Gets the service from the OSGi service registry.
*
* @param service The name of the service to get.
* @param numAttempts the number of attempts before the service is considered unavailable
* @param delayBetweenAttemptsMsec the delay (in milliseconds) between attempts to acquire the service
* @return the service object
* @throws RemoteOperationException if the service could not be got.
*/
protected Object getLocalSingletonService(String service, int numAttempts, int delayBetweenAttemptsMsec) {
ServiceReference<?>[] serviceReferences = null;
int attempt = 1;
while (attempt <= numAttempts) {
try {
serviceReferences = bundleContext.getServiceReferences(service, null);
} catch (InvalidSyntaxException e) {
log.error(ERROR_GET_SERVICE + service, e);
return null;
}
Object serviceObject;
if (serviceReferences != null && serviceReferences.length > 0) {
if (serviceReferences.length > 1) {
log.error("More than one OSGi service reference matched (request: service=" + service + ")");
return null;
}
serviceObject = bundleContext.getService(serviceReferences[0]);
if (serviceObject != null) {
// success
return serviceObject;
}
}
// prepare for retry
attempt++;
if (attempt <= numAttempts) {
log.warn(StringUtils.format("Failed to acquire OSGi service on attempt #%d; "
+ "it may not have started yet, retrying after %d msec (request: service=%s)", attempt - 1,
delayBetweenAttemptsMsec, service));
try {
Thread.sleep(delayBetweenAttemptsMsec);
} catch (InterruptedException e) {
log.error("Interrupted while waiting for retry");
return null;
}
}
}
log.error(ERROR_GET_SERVICE + service + " - service not available; made " + numAttempts + " attempt(s)");
return null;
}
}