package com.indyforge.foxnet.rmi.pattern.change.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import com.indyforge.foxnet.rmi.Invoker;
import com.indyforge.foxnet.rmi.RemoteInterfaces;
import com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer;
import com.indyforge.foxnet.rmi.pattern.change.Change;
import com.indyforge.foxnet.rmi.pattern.change.Changeable;
import com.indyforge.foxnet.rmi.pattern.change.ChangeableQueue;
import com.indyforge.foxnet.rmi.pattern.change.Session;
import com.indyforge.foxnet.rmi.pattern.change.SessionServer;
import com.indyforge.foxnet.rmi.util.Future;
import com.indyforge.foxnet.rmi.util.FutureCallback;
@RemoteInterfaces(SessionServer.class)
public final class DefaultSessionServer<T> implements AdminSessionServer<T> {
// Used to count the ids
private final AtomicLong counter = new AtomicLong(0);
// The maps which store the sessions
private final Map<Long, Session<T>> sessions = new HashMap<Long, Session<T>>();
// The cached session map
private Map<Long, Session<T>> cachedSessionMap;
// The cached sessions
private List<Session<T>> cachedSessions;
// The accepting sessions flag
private boolean acceptingSessions = true;
// The changeable local, broadcast and composite queue
private final ChangeableQueue<T> local,
broadcast = new DefaultChangeableQueue<T>(new Changeable<T>() {
@Override
public void applyChange(Change<T> change) {
for (Session<T> session : sessions()) {
try {
session.client().applyChange(change);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}), composite;
/**
* Creates a new session server using the changeable local.
*
* @param local
* The local.
*/
@SuppressWarnings("unchecked")
public DefaultSessionServer(Changeable<T> local) {
if (local == null) {
throw new NullPointerException("local");
}
// Create new local changeable queue
this.local = new DefaultChangeableQueue<T>(local);
// Create a new composite changeable
composite = new CompositeChangeableQueue<T>(this.local, broadcast);
}
/*
* (non-Javadoc)
*
* @see com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#
* isAcceptingSessions()
*/
@Override
public synchronized boolean isAcceptingSessions() {
return acceptingSessions;
}
/*
*
*/
@Override
public synchronized Session<T> session(long id) {
return sessions.get(id);
}
/*
* (non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#acceptingSessions
* (boolean)
*/
@Override
public synchronized void acceptingSessions(boolean acceptingSessions) {
this.acceptingSessions = acceptingSessions;
}
/*
* @Override(non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.SessionServer#openSession(com
* .indyforge.foxnet.rmi .pattern.change.Changeable, java.lang.String)
*/
public synchronized Session<T> openSession(Changeable<T> changeable,
String name) {
if (changeable == null) {
throw new NullPointerException("changeable");
}
/*
* If this server does not accept sessions, just return null.
*/
if (!acceptingSessions) {
return null;
}
// Calc new id
final long id = counter.getAndIncrement();
// Create new session
Session<T> s = new DefaultSession<T>(this, changeable, id, name);
// Put into map
sessions.put(id, s);
// Clear caches
cachedSessionMap = null;
cachedSessions = null;
// Register remover!
Invoker.of(changeable).manager().closeFuture()
.add(new FutureCallback() {
@Override
public void completed(Future future) throws Exception {
/*
* Synchronize with this server!
*/
synchronized (DefaultSessionServer.this) {
// Remove after disconnect
sessions.remove(id);
// Clear caches
cachedSessionMap = null;
cachedSessions = null;
}
}
});
return s;
}
/*
* (non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#sessionCount()
*/
@Override
public synchronized int sessionCount() {
return sessions.size();
}
/*
* (non-Javadoc)
*
* @see com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#local()
*/
@Override
public ChangeableQueue<T> local() {
return local;
}
/*
* (non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#broadcast()
*/
@Override
public ChangeableQueue<T> broadcast() {
return broadcast;
}
/*
* (non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#composite()
*/
@Override
public ChangeableQueue<T> composite() {
return composite;
}
/*
* (non-Javadoc)
*
* @see com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#names()
*/
@Override
public Map<Long, String> names() {
Map<Long, String> ptr = new HashMap<Long, String>();
for (Session<T> session : sessions()) {
ptr.put(session.id(), session.name());
}
return ptr;
}
/*
* (non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#sessionMap()
*/
@Override
public synchronized Map<Long, Session<T>> sessionMap() {
if (cachedSessionMap == null) {
cachedSessionMap = Collections
.unmodifiableMap(new HashMap<Long, Session<T>>(sessions));
}
return cachedSessionMap;
}
/*
* (non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#sessions()
*/
@Override
public synchronized List<Session<T>> sessions() {
if (cachedSessions == null) {
cachedSessions = Collections
.unmodifiableList(new ArrayList<Session<T>>(sessions
.values()));
}
return cachedSessions;
}
/*
* (non-Javadoc)
*
* @see
* com.indyforge.foxnet.rmi.pattern.change.AdminSessionServer#closeAll()
*/
@Override
public void closeAll() {
for (Session<T> session : sessions()) {
Invoker.of(session.client()).manager().close();
}
}
}