/* * Copyright 2010 NCHOVY * * Licensed 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.krakenapps.sentry.impl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Invalidate; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Validate; import org.krakenapps.rpc.RpcException; import org.krakenapps.rpc.RpcMethod; import org.krakenapps.rpc.RpcSession; import org.krakenapps.rpc.RpcSessionEvent; import org.krakenapps.rpc.SimpleRpcService; import org.krakenapps.sentry.SentryRpcService; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "sentry-rpc-service") @Provides public class SentryRpcServiceImpl extends SimpleRpcService implements SentryRpcService { private final Logger logger = LoggerFactory.getLogger(SentryRpcServiceImpl.class.getName()); private CommandHandlerTracker tracker; private ConcurrentMap<Object, AtomicInteger> serviceSet; private ConcurrentMap<String, Binding> rpcMap; private Set<String> features; public SentryRpcServiceImpl(BundleContext bc) { tracker = new CommandHandlerTracker(this, bc); rpcMap = new ConcurrentHashMap<String, Binding>(); serviceSet = new ConcurrentHashMap<Object, AtomicInteger>(); features = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); } @Validate public void start() { tracker.open(); } @Invalidate public void stop() { tracker.close(); } @Override public Collection<String> getFeatures() { return Collections.unmodifiableCollection(features); } @Override public void registerFeature(String feature) { features.add(feature); } @Override public void unregisterFeature(String feature) { features.remove(feature); } @Override public Collection<String> getMethods() { return new ArrayList<String>(rpcMap.keySet()); } @Override public void register(String rpcMethodName, Object service, String methodName) { Binding old = rpcMap.putIfAbsent(rpcMethodName, new Binding(service, methodName)); if (old != null) throw new IllegalStateException("sentry rpc name is already registered: " + rpcMethodName); AtomicInteger counter = new AtomicInteger(0); AtomicInteger oldCounter = serviceSet.putIfAbsent(service, counter); if (oldCounter != null) counter = oldCounter; counter.incrementAndGet(); logger.trace("kraken sentry: {} rpc method is registered", rpcMethodName); } @Override public void unregister(String rpcMethodName, Object service, String methodName) { if (!rpcMap.containsKey(rpcMethodName)) return; AtomicInteger counter = serviceSet.get(service); int refCount = counter.decrementAndGet(); if (refCount == 0) serviceSet.remove(service); Binding binding = rpcMap.get(rpcMethodName); if (binding.service == service && binding.methodName.equals(methodName)) { rpcMap.remove(rpcMethodName); logger.trace("kraken sentry: {} rpc method is unregistered", rpcMethodName); } } @Override public void sessionClosed(RpcSessionEvent e) { for (Object service : serviceSet.keySet()) { try { Method m = service.getClass().getMethod("sessionClosed", RpcSession.class); m.invoke(service, e.getSession()); } catch (NoSuchMethodException e1) { // ignore } catch (Exception e1) { logger.warn("cannot invoke sessionClosed callback", e1); } } } @RpcMethod(name = "run") public Object run(String methodName, Object[] params) { if (methodName.equals("getFeatures")) return new ArrayList<String>(features); if (logger.isDebugEnabled()) logger.debug("kraken sentry: call sentry method [{}]", methodName); Binding b = rpcMap.get(methodName); if (b == null) throw new RpcException("sentry method not found: " + methodName); try { Method[] methods = b.service.getClass().getMethods(); Method m = null; for (Method method : methods) { if (method.getName().equals(methodName)) { m = method; break; } } if (m == null) throw new NoSuchMethodException(methodName); return m.invoke(b.service, params); } catch (SecurityException e) { logger.error("kraken sentry: sentry rpc failed", e); throw new RpcException("security exception: " + e.getMessage()); } catch (NoSuchMethodException e) { logger.error("kraken sentry: sentry rpc failed", e); throw new RpcException("method not found: " + methodName); } catch (IllegalArgumentException e) { logger.error("kraken sentry: sentry rpc failed", e); throw new RpcException("illegal argument: " + e.getMessage()); } catch (IllegalAccessException e) { logger.error("kraken sentry: sentry rpc failed", e); throw new RpcException("illegal access: " + e.getMessage()); } catch (InvocationTargetException e) { logger.error("kraken sentry: sentry rpc failed", e.getTargetException()); throw new RpcException(e.getTargetException().getMessage()); } catch (Exception e) { logger.error("kraken sentry: sentry rpc failed", e); throw new RpcException(e.getMessage()); } } private static class Binding { public Object service; public String methodName; public Binding(Object service, String methodName) { this.service = service; this.methodName = methodName; } } }