/*
* 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.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
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.Requires;
import org.apache.felix.ipojo.annotations.Validate;
import org.krakenapps.api.KeyStoreManager;
import org.krakenapps.confdb.Config;
import org.krakenapps.confdb.ConfigCollection;
import org.krakenapps.confdb.ConfigDatabase;
import org.krakenapps.confdb.ConfigService;
import org.krakenapps.confdb.Predicates;
import org.krakenapps.log.api.Logger;
import org.krakenapps.log.api.LoggerFactory;
import org.krakenapps.log.api.LoggerFactoryRegistry;
import org.krakenapps.log.api.LoggerFactoryRegistryEventListener;
import org.krakenapps.log.api.LoggerRegistry;
import org.krakenapps.log.api.LoggerRegistryEventListener;
import org.krakenapps.rpc.RpcAgent;
import org.krakenapps.rpc.RpcConnection;
import org.krakenapps.rpc.RpcConnectionEventListener;
import org.krakenapps.rpc.RpcConnectionProperties;
import org.krakenapps.rpc.RpcSession;
import org.krakenapps.sentry.Base;
import org.krakenapps.sentry.CommandHandler;
import org.krakenapps.sentry.Sentry;
@Component(name = "sentry")
@Provides
public class SentryImpl implements Sentry, LoggerRegistryEventListener, LoggerFactoryRegistryEventListener {
private final org.slf4j.Logger slog = org.slf4j.LoggerFactory.getLogger(SentryImpl.class.getName());
@Requires
private RpcAgent agent;
@Requires
private ConfigService conf;
@Requires
private LoggerFactoryRegistry loggerFactoryRegistry;
@Requires
private LoggerRegistry loggerRegistry;
@Requires
private KeyStoreManager keyStoreManager;
/**
* base name to log session mappings
*/
private ConcurrentMap<String, RpcSession> logSessions;
/**
* base name to command session mappings
*/
private ConcurrentMap<String, RpcSession> commandSessions;
/**
* method name to command handler mappings
*/
private ConcurrentMap<String, CommandHandler> commandHandlers;
public SentryImpl() {
logSessions = new ConcurrentHashMap<String, RpcSession>();
commandSessions = new ConcurrentHashMap<String, RpcSession>();
commandHandlers = new ConcurrentHashMap<String, CommandHandler>();
}
@Validate
public void start() {
loggerFactoryRegistry.addListener(this);
loggerRegistry.addListener(this);
}
@Invalidate
public void stop() {
if (loggerRegistry != null)
loggerRegistry.removeListener(this);
if (loggerFactoryRegistry != null)
loggerFactoryRegistry.removeListener(this);
for (RpcSession session : commandSessions.values()) {
session.getConnection().close();
}
for (RpcSession session : logSessions.values()) {
session.getConnection().close();
}
commandSessions.clear();
logSessions.clear();
}
@Override
public String getGuid() {
ConfigDatabase db = conf.ensureDatabase("kraken-sentry");
ConfigCollection col = db.ensureCollection("sentry");
Config c = col.findOne(null);
if (c == null)
return null;
@SuppressWarnings("unchecked")
Map<String, Object> m = (Map<String, Object>) c.getDocument();
return (String) m.get("guid");
}
@Override
public void setGuid(String guid) {
ConfigDatabase db = conf.ensureDatabase("kraken-sentry");
ConfigCollection col = db.ensureCollection("sentry");
Config c = col.findOne(null);
if (c != null) {
@SuppressWarnings("unchecked")
Map<String, Object> m = (Map<String, Object>) c.getDocument();
m.put("guid", guid);
col.update(c);
} else {
Map<String, Object> m = new HashMap<String, Object>();
m.put("guid", guid);
col.add(m);
}
}
@Override
public String getBaseName(RpcConnection connection) {
for (Map.Entry<String, RpcSession> pair : commandSessions.entrySet())
if (pair.getValue().getConnection() == connection)
return pair.getKey();
return null;
}
@Override
public RpcSession getCommandSession(String baseName) {
return commandSessions.get(baseName);
}
@Override
public Collection<String> getCommandSessionNames() {
return Collections.unmodifiableCollection(commandSessions.keySet());
}
@Override
public void addCommandSession(final String name, RpcSession commandSession) {
if (name == null)
throw new IllegalArgumentException("name must be not null");
if (commandSession == null)
throw new IllegalArgumentException("connection must be not null");
RpcSession old = commandSessions.putIfAbsent(name, commandSession);
if (old != null)
throw new IllegalStateException("duplicated base connection name: " + name);
commandSession.getConnection().addListener(new RpcConnectionEventListener() {
@Override
public void connectionOpened(RpcConnection connection) {
}
@Override
public void connectionClosed(RpcConnection connection) {
commandSessions.remove(name);
}
});
}
@Override
public RpcSession removeCommandSession(String name) {
return commandSessions.remove(name);
}
@Override
public RpcSession getLogSession(String baseName) {
return logSessions.get(baseName);
}
@Override
public RpcSession connectLogChannel(final String baseName, String nonce) throws IOException {
Base base = getBase(baseName);
if (base == null)
throw new IllegalStateException("base config not found: " + baseName);
if (logSessions.containsKey(baseName))
return logSessions.get(baseName);
RpcConnection dataConnection = null;
try {
InetSocketAddress address = base.getAddress();
KeyManagerFactory kmf = keyStoreManager.getKeyManagerFactory("rpc-agent", "SunX509");
TrustManagerFactory tmf = keyStoreManager.getTrustManagerFactory("rpc-ca", "SunX509");
RpcConnectionProperties props = new RpcConnectionProperties(address, kmf, tmf);
dataConnection = agent.connectSsl(props);
dataConnection.addListener(new RpcConnectionEventListener() {
@Override
public void connectionClosed(RpcConnection connection) {
RpcSession session = commandSessions.get(baseName);
if (session != null && session.getConnection().isOpen())
session.getConnection().close();
logSessions.remove(baseName);
}
@Override
public void connectionOpened(RpcConnection connection) {
}
});
RpcSession logSession = dataConnection.createSession("kraken-base");
logSession.call("setLogChannel", new Object[] { getGuid(), nonce });
RpcSession old = logSessions.putIfAbsent(baseName, logSession);
if (old != null) {
if (dataConnection != null)
dataConnection.close();
return null;
}
return logSession;
} catch (Exception e) {
getCommandSession(baseName).getConnection().close();
if (dataConnection != null)
dataConnection.close();
slog.error("kraken-sentry: failed to open log channel", e);
throw new IOException(e.getMessage());
}
}
@Override
public void disconnectLogChannel(String baseName) {
RpcSession logSession = logSessions.remove(baseName);
RpcConnection conn = logSession.getConnection();
if (conn != null)
conn.close();
}
@Override
public Collection<Base> getBases() {
List<Base> l = new ArrayList<Base>();
ConfigDatabase db = conf.ensureDatabase("kraken-sentry");
for (BaseConfig base : db.findAll(BaseConfig.class).getDocuments(BaseConfig.class))
l.add(base);
return l;
}
@Override
public Base getBase(String baseName) {
ConfigDatabase db = conf.ensureDatabase("kraken-sentry");
Config c = db.findOne(BaseConfig.class, Predicates.field("name", baseName));
if (c != null)
return c.getDocument(BaseConfig.class);
return null;
}
@Override
public void addBase(Base base) {
BaseConfig config = new BaseConfig();
ConfigDatabase db = conf.ensureDatabase("kraken-sentry");
Base old = getBase(base.getName());
if (old != null)
throw new IllegalStateException("duplicated base name: " + base.getName());
config.setName(base.getName());
config.setIp(base.getAddress().getAddress().getHostAddress());
config.setPort(base.getAddress().getPort());
config.setKeyAlias(base.getKeyAlias());
config.setTrustAlias(base.getTrustAlias());
db.add(config);
}
@Override
public void removeBase(String name) {
ConfigDatabase db = conf.ensureDatabase("kraken-sentry");
Config c = db.findOne(BaseConfig.class, Predicates.field("name", name));
if (c == null)
return;
db.remove(c);
}
@Override
public void addCommandHandler(String name, CommandHandler handler) {
if (name == null)
throw new IllegalArgumentException("name must be not null");
if (handler == null)
throw new IllegalArgumentException("handler must be not null");
CommandHandler old = commandHandlers.putIfAbsent(name, handler);
if (old != null)
throw new IllegalStateException("kraken-sentry: duplicated command handler name => " + name);
}
@Override
public void removeCommandHandler(String name) {
if (name == null)
throw new IllegalArgumentException("name must be not null");
commandHandlers.remove(name);
}
//
// LoggerFactoryRegistryEventListener callbacks
//
@Override
public void factoryAdded(LoggerFactory factory) {
for (String baseName : commandSessions.keySet()) {
try {
RpcSession session = commandSessions.get(baseName);
session.post("factoryAdded", new Object[] { getGuid(), LoggerFactorySerializer.toMap(factory) });
} catch (Exception e) {
slog.error("kraken sentry: cannot post factory [{}] added event", factory.getFullName());
}
}
}
@Override
public void factoryRemoved(LoggerFactory factory) {
for (String baseName : commandSessions.keySet()) {
try {
RpcSession session = commandSessions.get(baseName);
session.post("factoryRemoved", new Object[] { getGuid(), LoggerFactorySerializer.toMap(factory) });
} catch (Exception e) {
slog.error("kraken sentry: cannot post factory [{}] removed event", factory.getFullName());
}
}
}
//
// LoggerRegistryEventListener callbacks
//
@Override
public void loggerAdded(Logger logger) {
for (String baseName : commandSessions.keySet()) {
try {
RpcSession session = commandSessions.get(baseName);
session.post("loggerAdded", new Object[] { getGuid(), LoggerFactorySerializer.toMap(logger) });
slog.info("kraken sentry: notify logger {}, {}", baseName, logger.getFullName());
} catch (Exception e) {
slog.error("kraken sentry: cannot post logger [{}] added event", logger.getFullName());
}
}
}
@Override
public void loggerRemoved(Logger logger) {
for (String baseName : commandSessions.keySet()) {
try {
RpcSession session = commandSessions.get(baseName);
session.post("loggerRemoved", new Object[] { getGuid(), logger.getName() });
} catch (Exception e) {
slog.error("kraken sentry: cannot post logger [{}] removed event", logger.getFullName());
}
}
}
}