/* * 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.base.impl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.krakenapps.base.RemoteLogger; import org.krakenapps.base.RemoteLoggerFactory; import org.krakenapps.base.RemoteLoggerFactoryInfo; import org.krakenapps.base.SentryProxy; import org.krakenapps.base.SentryProxyRegistry; import org.krakenapps.log.api.Logger; import org.krakenapps.log.api.LoggerFactory; import org.krakenapps.log.api.LoggerRegistry; import org.krakenapps.rpc.RpcAsyncCallback; import org.krakenapps.rpc.RpcConnection; import org.krakenapps.rpc.RpcException; import org.krakenapps.rpc.RpcSession; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; public class SentryProxyImpl implements SentryProxy { private final org.slf4j.Logger slogger = org.slf4j.LoggerFactory.getLogger(SentryProxyImpl.class.getName()); private boolean isClosed = false; private BundleContext bc; private String guid; private RpcSession commandSession; private String nonce; private RpcSession logSession; private ConcurrentMap<String, Logger> connectedLoggers; private LoggerRegistry loggerRegistry; private SentryProxyRegistry sentryProxyRegistry; /** * remote factory full name to factory registration mappings */ private ConcurrentMap<String, ServiceRegistration> factoryMap; private ConcurrentMap<String, Logger> loggerMap; public SentryProxyImpl(BundleContext bc, String guid, RpcSession commandSession, LoggerRegistry loggerRegistry, SentryProxyRegistry sentryProxyRegistry) { this.bc = bc; this.guid = guid; this.commandSession = commandSession; this.connectedLoggers = new ConcurrentHashMap<String, Logger>(); this.loggerRegistry = loggerRegistry; this.sentryProxyRegistry = sentryProxyRegistry; this.factoryMap = new ConcurrentHashMap<String, ServiceRegistration>(); this.loggerMap = new ConcurrentHashMap<String, Logger>(); } @Override public boolean isOpen() { return !isClosed; } @Override public String getGuid() { return guid; } @Override public Object call(String method, Object[] params) throws RpcException, InterruptedException { verify(); return commandSession.call("run", new Object[] { method, params }); } @Override public Object call(String method, Object[] params, long timeout) throws RpcException, InterruptedException { verify(); return commandSession.call("run", new Object[] { method, params }, timeout); } @Override public void call(String method, Object[] params, RpcAsyncCallback callback) { verify(); commandSession.call("run", new Object[] { method, params }, callback); } @Override public void post(String method, Object[] params) { verify(); commandSession.post("run", new Object[] { method, params }); } @Override public void requestLogChannel() { verify(); if (nonce != null) throw new IllegalStateException("already requested log channel"); if (logSession != null) throw new IllegalStateException("log session already exists"); nonce = UUID.randomUUID().toString(); post("connectLogChannel", new Object[] { nonce }); } @Override public RpcSession getLogSession() { verify(); return logSession; } @Override public void setLogSession(String nonce, RpcSession logSession) { verify(); if (!this.nonce.equals(nonce)) throw new IllegalArgumentException("nonce does not match"); this.logSession = logSession; } @Override public Map<String, RemoteLoggerFactoryInfo> getRemoteLoggerFactories() throws RpcException, InterruptedException { Object[] factories = (Object[]) call("getLoggerFactories", null); return LoggerFactoryResponseParser.parseFactories(factories); } @SuppressWarnings("unchecked") @Override public RemoteLoggerFactoryInfo getRemoteLoggerFactory(String name) throws RpcException, InterruptedException { Object factory = call("getLoggerFactory", new Object[] { name }); return LoggerFactoryResponseParser.parseFactory((Map<String, Object>) factory); } @Override public Map<String, Logger> getRemoteLoggers() throws RpcException, InterruptedException { Object[] loggers = (Object[]) call("getLoggers", null); return LoggerResponseParser.parse(this, loggers); } @Override public Logger createRemoteLogger(String factoryName, String name, String description, Properties props) throws RpcException, InterruptedException { Map<String, Object> m = new HashMap<String, Object>(); for (Object key : props.keySet()) { m.put(key.toString(), props.get(key)); } call("createLogger", new Object[] { factoryName, name, description, m }); return new RemoteLogger(this, name, factoryName, description, new Properties()); } @Override public void removeRemoteLogger(String name) throws RpcException, InterruptedException { call("removeLogger", new Object[] { name }); } @Override public void startRemoteLogger(String name, int interval) throws RpcException, InterruptedException { call("startLogger", new Object[] { name, interval }); } @Override public void stopRemoteLogger(String name, int timeout) throws RpcException, InterruptedException { call("stopLogger", new Object[] { name, timeout }); } @Override public void connectRemoteLogger(String loggerName) throws RpcException, InterruptedException { if (loggerName == null) throw new IllegalArgumentException("logger name must not be null"); if (connectedLoggers.containsKey(loggerName)) throw new IllegalStateException("already connected logger"); Logger remoteLogger = loggerRegistry.getLogger(guid + "\\" + loggerName); Logger old = connectedLoggers.putIfAbsent(loggerName, remoteLogger); if (old != null) throw new IllegalStateException("duplicated logger connection: " + loggerName); call("connectLogger", new Object[] { loggerName }); } @Override public void disconnectRemoteLogger(String loggerName) throws RpcException, InterruptedException { if (loggerName == null) throw new IllegalArgumentException("logger name must not be null"); Logger logger = loggerRegistry.getLogger(guid + "\\" + loggerName); if (logger == null) throw new IllegalStateException("logger not found: " + loggerName); call("disconnectLogger", new Object[] { loggerName }); } @Override public Logger getConnectedLogger(String name) { if (name == null) throw new IllegalArgumentException("connected logger name must not be null"); return connectedLoggers.get(name); } @Override public Collection<Logger> getConnectedLoggers() { return new ArrayList<Logger>(connectedLoggers.values()); } @Override public void syncLoggerFactories() throws RpcException, InterruptedException { Map<String, RemoteLoggerFactoryInfo> map = getRemoteLoggerFactories(); unregisterAllRemoteFactories(); for (String name : map.keySet()) { RemoteLoggerFactoryInfo info = map.get(name); registerLoggerFactory(info); } } @Override public void syncLoggers() throws RpcException, InterruptedException { Map<String, Logger> loggers = getRemoteLoggers(); unregisterAllRemoteLoggers(); for (String name : loggers.keySet()) { Logger logger = loggers.get(name); loggerRegistry.addLogger(logger); } } private void unregisterAllRemoteLoggers() { for (String loggerName : new ArrayList<String>(this.loggerMap.keySet())) { Logger logger = loggerMap.get(loggerName); try { unregisterLogger(logger.getName()); } catch (Exception e) { // all remote logger should be removed } } } private void unregisterAllRemoteFactories() { for (String factoryName : new ArrayList<String>(this.factoryMap.keySet())) { try { ServiceRegistration registration = this.factoryMap.get(factoryName); RemoteLoggerFactory factory = (RemoteLoggerFactory) bc.getService(registration.getReference()); unregisterLoggerFactory(factory.getFullName()); } catch (Exception e) { // all remote logger factories should be removed } } } @Override public void registerLoggerFactory(RemoteLoggerFactoryInfo factory) { RemoteLoggerFactory remoteFactory = new RemoteLoggerFactory(this, factory); String factoryFullName = guid + "\\" + factory.getName(); if (factoryMap.containsKey(factoryFullName)) { slogger.error("kraken base: factory [{}] already added", factoryFullName); return; } try { ServiceRegistration registration = bc.registerService(LoggerFactory.class.getName(), remoteFactory, new Hashtable<String, String>()); ServiceRegistration old = factoryMap.putIfAbsent(factoryFullName, registration); if (old != null) registration.unregister(); slogger.info("kraken base: factory [{}] added", factoryFullName); } catch (Exception e) { slogger.error("kraken base: bundle context is not valid", e); } } @Override public void unregisterLoggerFactory(String factoryFullName) { ServiceRegistration old = factoryMap.remove(factoryFullName); if (old == null) { slogger.error("kraken base: factory [{}] not found", factoryFullName); return; } old.unregister(); slogger.info("kraken base: factory [{}] removed", factoryFullName); } @Override public void registerLogger(Logger logger) { loggerRegistry.addLogger(logger); loggerMap.put(logger.getName(), logger); } @Override public void unregisterLogger(String loggerName) { if (loggerName == null) throw new IllegalArgumentException("logger must not be null"); connectedLoggers.remove(loggerName); Logger logger = loggerRegistry.getLogger(guid + "\\" + loggerName); try { loggerRegistry.removeLogger(logger); } catch (Exception e) { // if connection is reset, removeLogger can throw 'logger is still // running' exception. However it should be ignored. } loggerMap.remove(loggerName); } @Override public void loggerStarted(String loggerFullName, int interval) { RemoteLogger logger = (RemoteLogger) loggerRegistry.getLogger(loggerFullName); if (logger == null) return; logger.setRunning(true); logger.setInterval(interval); } @Override public void loggerStopped(String loggerFullName) { RemoteLogger logger = (RemoteLogger) loggerRegistry.getLogger(loggerFullName); if (logger == null) return; logger.setRunning(false); } @Override public void close() { if (isClosed) return; isClosed = true; if (sentryProxyRegistry != null) sentryProxyRegistry.unregister(this); // unregister all remote logger and factories unregisterAllRemoteLoggers(); unregisterAllRemoteFactories(); // close all sessions if (commandSession != null) { commandSession.getConnection().close(); commandSession = null; } if (logSession != null) { logSession.getConnection().close(); logSession = null; } connectedLoggers.clear(); } private void verify() { if (isClosed) throw new IllegalStateException("sentry [" + guid + "] is closed"); } @Override public String toString() { RpcConnection connection = commandSession.getConnection(); return String.format("guid=%s, remote=%s", guid, connection.getRemoteAddress()); } }