/******************************************************************************* * Copyright 2016 Specure GmbH * * 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 at.alladin.rmbt.qos.testserver.servers; import java.net.InetAddress; import java.net.SocketTimeoutException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import at.alladin.rmbt.qos.testserver.ServerPreferences.TestServerServiceEnum; import at.alladin.rmbt.qos.testserver.entity.TestCandidate; import at.alladin.rmbt.qos.testserver.util.TestServerConsole; /** * * @author lb * */ public abstract class AbstractServer<T extends AutoCloseable, H extends TestCandidate> implements Runnable { //////////////////////////////////////////////// // fields: //////////////////////////////////////////////// private final String name; private final TestServerServiceEnum testServerService; protected final Class<T> serverSocketTypeClazz; protected final Class<H> clientDataHolderClazz; /** * holds the candidates for this socket */ protected final ConcurrentHashMap<InetAddress, H> candidateMap = new ConcurrentHashMap<>(); /** * time to live of this server */ protected final AtomicLong ttlTimestamp = new AtomicLong(0); /** * the server socket */ protected T serverSocket; /** * the port of this socket */ protected final int port; /** * */ protected final InetAddress inetAddr; //////////////////////////////////////////////// // constructors: //////////////////////////////////////////////// public AbstractServer(Class<T> serverSocketTypeClazz, Class<H> clientHandlerRunnableClazz, InetAddress inetAddr, int port, String tag, TestServerServiceEnum testServerService) { this.serverSocketTypeClazz = serverSocketTypeClazz; this.clientDataHolderClazz = clientHandlerRunnableClazz; this.name = tag + " [" + inetAddr + ":" + port + "]"; this.port = port; this.inetAddr = inetAddr; this.testServerService = testServerService; } //////////////////////////////////////////////// // abstract methods: //////////////////////////////////////////////// public abstract void prepare() throws Exception; public abstract boolean close() throws Exception; public abstract boolean isAlive(); protected abstract void execute() throws Exception; //////////////////////////////////////////////// // getter/setter methods: //////////////////////////////////////////////// public T getServerSocket() { return serverSocket; } public void setServerSocket(T serverSocket) { this.serverSocket = serverSocket; } public String getName() { return name; } public AtomicLong getTtlTimestamp() { return ttlTimestamp; } public void refreshTtl(long byValue) { ttlTimestamp.set(System.currentTimeMillis() + byValue); TestServerConsole.log(getName() + " Refreshing TTL to: " + ttlTimestamp.get(), 2, testServerService); } public int getPort() { return port; } public InetAddress getInetAddr() { return inetAddr; } //////////////////////////////////////////////// // methods: //////////////////////////////////////////////// protected synchronized void closeSocket() { try { this.serverSocket.close(); this.serverSocket = null; } catch (Exception e) { TestServerConsole.error(getName() + " could not close socket", e, 2, testServerService); } } @Override public void run() { TestServerConsole.log(getName() + " started!" , 2, testServerService); try { while(isAlive()) { execute(); } } catch (SocketTimeoutException e) { TestServerConsole.error(getName(), e, 2, testServerService); } catch (Exception e) { TestServerConsole.error(getName(), e, 1, testServerService); } finally { try { //close this socket if necessary close(); } catch (Exception e) { e.printStackTrace(); } } TestServerConsole.log(getName() + " closed!", 0, testServerService); } /** * * @param candidateInetAddress * @throws IllegalAccessException * @throws InstantiationException */ @SuppressWarnings("unchecked") public synchronized TestCandidate registerCandidate(InetAddress candidateInetAddress, int resetTtl) { try { TestCandidate candidate = null; if (candidateMap.containsKey(candidateInetAddress)) { candidate = (TestCandidate) candidateMap.get(candidateInetAddress); } else { candidate = clientDataHolderClazz.newInstance(); candidate.setTtl(resetTtl); } candidate.increaseTestCounter(true); TestServerConsole.log(getName() + " Registering candidate " + candidateInetAddress + ": " + candidate + ")", 1, testServerService); candidateMap.put(candidateInetAddress, (H) candidate); return candidate; } catch (Exception e) { TestServerConsole.error(getName(), e, 2, testServerService); return null; } } /** * * @param candidateInetAddress */ public synchronized void removeCandidate(InetAddress candidateInetAddress) { if (candidateMap.containsKey(candidateInetAddress)) { TestCandidate candidate = candidateMap.get(candidateInetAddress); if (candidate.decreaseTestCounter(true) <= 0) { TestServerConsole.log(getName() + " Candidate " + candidateInetAddress + " has no more tests left.", 1, testServerService); candidateMap.remove(candidateInetAddress); } else { TestServerConsole.log(getName() + " Candidate (" + candidate + " - " + candidateInetAddress + ") has " + (candidate.getTestCounter()) + " tests left.", 1, testServerService); } } } /** * * @return */ public Map<InetAddress, H> getCandidateMap() { return candidateMap; } }