/* * Copyright 2011 Future Systems * * 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.portmon.impl; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Provides; import org.krakenapps.cron.PeriodicJob; import org.krakenapps.portmon.PortEventListener; import org.krakenapps.portmon.PortMonitor; import org.krakenapps.portmon.PortStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @PeriodicJob("* * * * *") @Component(name = "port-monitor") @Provides public class PortMonitorImpl implements PortMonitor, Runnable { private ConcurrentMap<InetSocketAddress, LastStatus> tcpTargets; private Set<PortEventListener> callbacks; private int timeout; private ExecutorService threadPool; public PortMonitorImpl() { tcpTargets = new ConcurrentHashMap<InetSocketAddress, LastStatus>(); callbacks = Collections.newSetFromMap(new ConcurrentHashMap<PortEventListener, Boolean>()); timeout = 10000; // 10seconds threadPool = Executors.newCachedThreadPool(); } @Override public void setTimeout(int milliseconds) { this.timeout = milliseconds; } @Override public Collection<InetSocketAddress> getTcpTargets() { return new ArrayList<InetSocketAddress>(tcpTargets.keySet()); } @Override public PortStatus getTcpPortStatus(InetSocketAddress target) { return tcpTargets.get(target); } @Override public void run() { List<Callable<Void>> tasks = new ArrayList<Callable<Void>>(tcpTargets.size()); for (InetSocketAddress target : tcpTargets.keySet()) { TcpChecker checker = new TcpChecker(target, timeout); tasks.add(checker); } try { threadPool.invokeAll(tasks); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void addTcpTarget(InetSocketAddress target) { if (target == null) throw new IllegalArgumentException("target must be not null"); tcpTargets.put(target, new LastStatus()); } @Override public void removeTcpTarget(InetSocketAddress target) { if (target == null) throw new IllegalArgumentException("target must be not null"); tcpTargets.remove(target); } @Override public void addListener(PortEventListener callback) { if (callback == null) throw new IllegalArgumentException("callback must be not null"); callbacks.add(callback); } @Override public void removeListener(PortEventListener callback) { if (callback == null) throw new IllegalArgumentException("callback must be not null"); callbacks.remove(callback); } private static class LastStatus implements PortStatus { public Date check; public boolean status; public Date success; public Date fail; @Override public Date getLastCheckTime() { return check; } @Override public boolean getLastStatus() { return status; } @Override public String toString() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return String.format("status=%s, check=%s", status ? "up" : "down", check == null ? "none" : dateFormat .format(check)); } } private void fireTcpConnectCallback(InetSocketAddress target, int connectTime) { for (PortEventListener callback : callbacks) { callback.onConnect(target, connectTime); } } private void fireTcpConnectRefusedCallback(InetSocketAddress target, int timeout, IOException e) { for (PortEventListener callback : callbacks) { callback.onConnectRefused(target, timeout, e); } } private class TcpChecker implements Callable<Void> { private final Logger logger = LoggerFactory.getLogger(TcpChecker.class.getName()); private InetSocketAddress target; private int timeout; public TcpChecker(InetSocketAddress target, int timeout) { this.target = target; this.timeout = timeout; } @Override public Void call() { LastStatus status = tcpTargets.get(target); if (status == null) return null; Date beginDate = new Date(); status.check = beginDate; Socket socket = new Socket(); try { logger.trace("kraken-portmon: try to connect {}", target); long begin = new Date().getTime(); socket.connect(target, timeout); long end = new Date().getTime(); logger.trace("kraken-portmon: {} connected", target); socket.close(); status.success = beginDate; status.status = true; fireTcpConnectCallback(target, (int) (end - begin)); } catch (IOException e) { status.fail = beginDate; status.status = false; fireTcpConnectRefusedCallback(target, timeout, e); } return null; } } }