package org.bundlebee.weaver; import org.bundlebee.registry.Registry; import org.bundlebee.registry.impl.RegistryImpl; import org.bundlebee.remoteservicecall.BundleLifecycleClient; import org.bundlebee.remoteservicecall.RemotingException; import org.osgi.framework.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.*; import java.util.*; import org.bundlebee.remoteservicecall.Call; import org.bundlebee.remoteservicecall.StaticCallContext; import org.bundlebee.remoteservicecall.CallDescriptor; import org.bundlebee.remoteservicecall.Caller; /** * * * @author Hendrik Schreiber * @author Jörg Plewe */ public class ServiceCallAspect { private final static Logger LOG = LoggerFactory.getLogger(ServiceCallAspect.class); private final static ServiceCallStats serviceCallStats = new ServiceCallStats(); private final static Registry registry; private final static ServiceCallDispatchStrategy serviceCallDispatchStrategy; public final static URI LOCAL_URI; // // static class setup // static { // // determine 'local URI' // URI uri = null; try { uri = new URI("vm://" + RegistryImpl.getInstance().getNodeId() + "/"); } catch (URISyntaxException e) { LOG.error(e.toString(), e); // // exception must not occur! // throw new RuntimeException(e); } LOCAL_URI = uri; // get hold of registry registry = RegistryImpl.getInstance(); // create a registry serviceCallDispatchStrategy = DispatchStrategyFactory.create(registry, serviceCallStats); } /** * ctor */ private ServiceCallAspect() { } /** * retrieve statistics * @return ServiceCallStats */ public static ServiceCallStats getServiceCallStats() { return serviceCallStats; } private static void unregisterManager( URI uri, Exception e) { try { registry.unregisterManager(uri.toURL()); LOG.info("Manager at " + uri + " has been blacklisted/unregistered because of a " + e.toString()); } catch (MalformedURLException e1) { LOG.error(e1.toString(), e1); } } private static void logLocalCall(final Object service, final String methodName, final Class[] parameterTypes, final long duration) { if (LOG.isDebugEnabled()) LOG.debug("Local call: " + duration + " nano seconds"); serviceCallStats.logLocalCall(service, methodName, parameterTypes, duration); } private static void logRemoteCall(final URI uri, final Object service, final String methodName, final Class[] parameterTypes, final long duration) { if (LOG.isDebugEnabled()) LOG.debug("Remote call to " + uri +": " + duration + " nano seconds"); serviceCallStats.logCall(uri, service, methodName, parameterTypes, duration); } private static String getServiceClassName(final Object localService) { return localService.getClass().getName(); } private static String getServiceFilter(final Object localService, final String methodname, final Class[] parameterTypes) { final List possibleObjectClassNames = new ArrayList(); possibleObjectClassNames.add(localService.getClass().getName()); for (final Class iface:localService.getClass().getInterfaces()) { try { iface.getMethod(methodname, parameterTypes); possibleObjectClassNames.add(iface.getName()); } catch (NoSuchMethodException e) { // ignore on purpose } } final String serviceFilter; if (possibleObjectClassNames.size() <= 1) { serviceFilter = null; } else { final StringBuilder sb = new StringBuilder(); sb.append("(|"); for (final String objectClassName:possibleObjectClassNames) { sb.append('('); sb.append(Constants.OBJECTCLASS); sb.append('='); sb.append(objectClassName); sb.append(')'); } sb.append(')'); serviceFilter = sb.toString(); } return serviceFilter; } /** Tagging object describing that a remote call could not happen for some reason. */ public final static Object CANNOT_EXECUTE_REMOTELY = new Object(); /** * Try to do a remote call. * * @param service * @param methodname * @param bundleSymbolicNameVersion * @param paramtypes * @param args * @return remote result (can be null) or CANNOT_EXECUTE_REMOTELY */ public static Object tryRemote( Object service, String methodname, String bundleSymbolicNameVersion, Class[] paramtypes, Object[] args ) throws Exception { // if the static context says I may not go remote, I don't if (StaticCallContext.isForceLocal()) { // clear the flag so that subsequent calls in this thread can be remoted again StaticCallContext.clearForceLocal(); // remember when the local call is going to start StaticCallContext.setTimeStamp(); return CANNOT_EXECUTE_REMOTELY; } // timestamp to measure remote execution long remoteStartTime = System.nanoTime(); // // get a service filter and classname. The latter is only used if no filter is set // String serviceFilter = getServiceFilter(service, methodname, paramtypes); String serviceClassName = (null == serviceFilter) ? getServiceClassName(service) : null; URI mgrURI = null; URL serviceURL = null; try { // // retrieve the URL where to find the service // mgrURI = serviceCallDispatchStrategy.getManagerURI(bundleSymbolicNameVersion, service, methodname, paramtypes); serviceURL = new URL(BundleLifecycleClient.toDirectoryURL(mgrURI.toURL()), "service"); if (LOG.isDebugEnabled()) { LOG.debug("Service URL: " + serviceURL); } } catch (MalformedURLException ex) { // shouldn't happen throw new RuntimeException(ex); } try { CallDescriptor desc = new CallDescriptor(serviceClassName, serviceFilter, methodname, paramtypes); Call c = new Call(RegistryImpl.getInstance().getNodeId(), System.identityHashCode(service), desc, args); Object ret = Caller.executeCall(c, serviceURL, service.getClass().getClassLoader()); // update statistics logRemoteCall( mgrURI, service, methodname, paramtypes, System.nanoTime()-remoteStartTime ); return ret; } catch (RemotingException ex) { // // @todo something went wrong on remoting, have to react? // unregisterManager(mgrURI, ex); } catch (Exception ex) { // // ex either is: // * a RemoteTargetException thrown intentionally by the worker code // * a RuntimeException caused by BundleBee errors // throw ex; } // remember when the local call is going to start StaticCallContext.setTimeStamp(); return CANNOT_EXECUTE_REMOTELY; } /** * Call this at the end of a local call. Depends on the fact that {@link tryRemote(Object,String,String,Class[],Object[])} has been called before. * * @param service * @param methodname * @param paramtypes */ public static void finishLocal( Object service, String methodname, Class[] paramtypes ) { logLocalCall( service, methodname, paramtypes, System.nanoTime()-StaticCallContext.getTimeStamp() ); } }