/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JAME is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.queue.network;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Logger;
import net.sf.jame.core.util.DefaultThreadFactory;
/**
* @author Andrea Medeghini
*/
public class LocalService {
private static final Logger logger = Logger.getLogger(LocalService.class.getName());
private static final ThreadFactory factory = new DefaultThreadFactory("Thread", true, Thread.MIN_PRIORITY);
private final HashMap<String, LocalServiceSession> serverSessions = new HashMap<String, LocalServiceSession>();
private final HashMap<String, LocalServiceSession> clientSessions = new HashMap<String, LocalServiceSession>();
private final ServiceProcessor processor;
private final String serviceName;
private Thread thread;
private boolean running;
private boolean dirty;
private final Object monitor = new Object();
/**
* @param serviceName
* @param processor
*/
public LocalService(final String serviceName, final ServiceProcessor processor) {
this.serviceName = "LocalService " + serviceName;
this.processor = processor;
}
/**
* @return the name
*/
public String getServiceName() {
return serviceName;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("serviceName = ");
builder.append(serviceName);
return builder.toString();
}
/**
*
*/
public void start() {
processor.start();
if (thread == null) {
thread = factory.newThread(new CleanupHandler());
thread.setName("LocalService Cleanup Thread");
running = true;
thread.start();
}
}
/**
*
*/
public void stop() {
processor.stop();
if (thread != null) {
running = false;
thread.interrupt();
try {
thread.join();
}
catch (final InterruptedException e) {
}
thread = null;
}
}
/**
* @see net.sf.jame.queue.network.ServiceEndpoint#createSession(net.sf.jame.queue.network.ServiceListener)
*/
public ServiceSession createSession(final ServiceListener listener) throws ServiceException {
final SessionHandler sessionHandler = processor.createSessionHandler();
final ServiceConsumer clientConsumer = new LocalServiceConsumer(listener);
final ServiceConsumer serverConsumer = new LocalServiceConsumer(sessionHandler);
final LocalServiceSession clientSession = new LocalServiceSession(SessionIDFactory.newSessionId(), clientConsumer, new LocalServiceProducer(serverConsumer));
logger.info("Local client session created " + clientSession.getSessionId());
synchronized (clientSessions) {
clientSessions.put(clientSession.getSessionId(), clientSession);
}
final LocalServiceSession serverSession = new LocalServiceSession(SessionIDFactory.newSessionId(), serverConsumer, new LocalServiceProducer(clientConsumer));
logger.info("Local server session created " + serverSession.getSessionId());
sessionHandler.setSession(serverSession);
synchronized (serverSessions) {
serverSessions.put(serverSession.getSessionId(), serverSession);
}
return clientSession;
}
private class LocalServiceSession extends ServiceSession {
/**
* @param sessionId
* @param consumer
* @param producer
*/
public LocalServiceSession(final String sessionId, final ServiceConsumer consumer, final ServiceProducer producer) {
super(sessionId, consumer, producer);
}
/**
* @see net.sf.jame.queue.network.ServiceSession#dispose()
*/
@Override
public void dispose() {
logger.fine("Session disposed " + getSessionId());
super.dispose();
synchronized (monitor) {
dirty = true;
monitor.notify();
}
}
/**
* @see net.sf.jame.queue.network.ServiceSession#isLocalSession()
*/
@Override
public boolean isLocalSession() {
return true;
}
/**
* @param message
* @throws Exception
*/
public void onMessage(final ServiceMessage message) throws ServiceException {
if (isExpired()) {
throw new ServiceException("Expired session " + sessionId);
}
consumer.onMessage(message);
}
/**
* @param message
* @throws Exception
*/
public void consumeMessage(final ServiceMessage message) throws ServiceException {
if (isExpired()) {
throw new ServiceException("Expired session " + sessionId);
}
consumer.consumeMessage(message);
}
/**
* @throws Exception
*/
public void consumeMessages() throws ServiceException {
if (isExpired()) {
throw new ServiceException("Expired session " + sessionId);
}
consumer.consumeMessages();
}
}
private class LocalServiceProducer implements ServiceProducer {
private ServiceListener listener;
/**
* @param listener
*/
public LocalServiceProducer(final ServiceListener listener) {
this.listener = listener;
}
/**
* @see net.sf.jame.queue.network.ServiceProducer#dispose()
*/
public void dispose() {
listener = null;
}
/**
* @see net.sf.jame.queue.network.ServiceProducer#sendMessage(net.sf.jame.queue.network.ServiceMessage)
*/
public void sendMessage(final ServiceMessage message) throws ServiceException {
listener.onMessage(message);
}
/**
* @see net.sf.jame.queue.network.ServiceProducer#sendKeepAliveMessage()
*/
public void sendKeepAliveMessage() throws ServiceException {
((LocalServiceConsumer) listener).lastMessageTime = System.currentTimeMillis();
}
/**
* @see net.sf.jame.queue.network.ServiceProducer#sendAckMessage()
*/
public void sendAckMessage() throws ServiceException {
}
}
private class LocalServiceConsumer implements ServiceConsumer {
private ServiceListener listener;
private long lastMessageTime = System.currentTimeMillis();
/**
* @param listener
*/
public LocalServiceConsumer(final ServiceListener listener) {
this.listener = listener;
}
/**
* @see net.sf.jame.queue.network.ServiceConsumer#consumeMessages()
*/
public void consumeMessages() throws ServiceException {
}
/**
* @see net.sf.jame.queue.network.ServiceConsumer#dispose()
*/
public void dispose() {
listener = null;
}
/**
* @see net.sf.jame.queue.network.ServiceConsumer#isTimeout()
*/
public boolean isTimeout() {
return (System.currentTimeMillis() - lastMessageTime) > 120 * 1000L;
}
/**
* @see net.sf.jame.queue.network.ServiceListener#onMessage(net.sf.jame.queue.network.ServiceMessage)
*/
public void onMessage(final ServiceMessage message) throws ServiceException {
if (listener != null) {
lastMessageTime = System.currentTimeMillis();
listener.onMessage(message);
}
}
/**
* @see net.sf.jame.queue.network.ServiceConsumer#consumeMessage(net.sf.jame.queue.network.ServiceMessage)
*/
public void consumeMessage(final ServiceMessage message) throws ServiceException {
if (listener != null) {
lastMessageTime = System.currentTimeMillis();
listener.onMessage(message);
}
}
}
private class CleanupHandler implements Runnable {
/**
* @see java.lang.Runnable#run()
*/
public void run() {
final long pollingTime = 30 * 1000L;
try {
while (running) {
try {
synchronized (serverSessions) {
final Iterator<LocalServiceSession> sessionIterator = serverSessions.values().iterator();
while (sessionIterator.hasNext()) {
final LocalServiceSession session = sessionIterator.next();
if (session.isExpired()) {
logger.info("Dispose expired session " + session.getSessionId());
sessionIterator.remove();
}
else {
try {
session.consumeMessages();
}
catch (final Exception e) {
e.printStackTrace();
session.invalidate();
}
}
}
}
synchronized (clientSessions) {
final Iterator<LocalServiceSession> sessionIterator = clientSessions.values().iterator();
while (sessionIterator.hasNext()) {
final LocalServiceSession session = sessionIterator.next();
if (session.isExpired()) {
logger.info("Dispose expired session " + session.getSessionId());
sessionIterator.remove();
}
else {
try {
session.consumeMessages();
}
catch (final Exception e) {
e.printStackTrace();
session.invalidate();
}
}
}
}
}
catch (final Exception e) {
e.printStackTrace();
}
synchronized (monitor) {
if (!dirty) {
monitor.wait(pollingTime);
}
dirty = false;
}
}
}
catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
synchronized (serverSessions) {
for (final ServiceSession session : serverSessions.values()) {
logger.info("Dispose session " + session.getSessionId());
session.dispose();
}
serverSessions.clear();
}
synchronized (clientSessions) {
for (final ServiceSession session : clientSessions.values()) {
logger.info("Dispose session " + session.getSessionId());
session.dispose();
}
clientSessions.clear();
}
}
}
}
}