/*
* $Id: ClientSessionContainer.java,v 1.6 2002/09/16 08:05:07 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.session.net;
import java.util.Enumeration;
import java.net.Socket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.BufferedOutputStream;
import anvil.server.Zone;
import anvil.server.ConfigurationError;
import anvil.server.SessionContainerPreferences;
import anvil.session.AbstractSessionEventHandler;
import anvil.session.SessionContainer;
import anvil.session.Session;
import anvil.java.io.GenericInputStream;
import anvil.java.util.Hashlist;
import anvil.java.util.BindingEnumeration;
import anvil.core.Any;
import anvil.core.Serialization;
import anvil.core.UnserializationException;
import anvil.util.Conversions;
/**
* interface ClientSessionContainer
*
* @author: Jani Lehtim�ki
*/
public class ClientSessionContainer extends AbstractSessionEventHandler implements SessionContainer
{
protected InetAddress _address;
protected int _port;
protected Socket _socket;
protected GenericInputStream _input;
protected PrintStream _output;
protected Object _lock = new Object();
protected Hashlist _sessions = new Hashlist();
protected SessionCleaner _cleaner;
public ClientSessionContainer()
{
}
public void initialize(Zone zone)
{
super.initialize(zone);
SessionContainerPreferences prefs = zone.getSessionContainerPreferences();
String hostname = prefs.getPreference("host", "localhost").toString();
try {
_address = InetAddress.getByName(hostname);
} catch (UnknownHostException e) {
throw new ConfigurationError("Unknown host '"+hostname+"'");
}
_port = prefs.getIntPreference("port", 9000);
_cleaner = new SessionCleaner("Cleaner-"+_port, prefs.getCheckoutFrequency());
_cleaner.setDaemon(true);
_cleaner.start();
_zone.log().info("SessionContainer " + this + " initialized");
}
public void stop()
{
closeConnection();
_cleaner.stop();
_cleaner = null;
super.stop();
_zone.log().info("SessionContainer " + this + " stopped");
}
protected synchronized void openConnection()
{
if (_input != null) {
return;
}
try {
_socket = new Socket(_address, _port);
_input = new GenericInputStream(_socket.getInputStream());
_output = new PrintStream(new BufferedOutputStream(_socket.getOutputStream()));
} catch (IOException e) {
closeConnection();
throw new RuntimeException("Couldn't establish connection to remote session container: "+e);
}
}
protected synchronized void closeConnection()
{
try {
if (_output != null) {
_output.close();
}
} catch (Throwable t) {
}
try {
if (_input != null) {
_input.close();
}
} catch (Throwable t) {
}
try {
if (_socket != null) {
_socket.close();
}
} catch (Throwable t) {
}
_input = null;
_output = null;
_socket = null;
}
protected void flush()
{
_output.flush();
if (_output.checkError()) {
closeConnection();
throw new RuntimeException("IO error while writing to remote session container");
}
}
protected void writeLine(String line)
{
_output.println(line);
if (_output.checkError()) {
closeConnection();
throw new RuntimeException("IO error while writing to remote session container");
}
}
protected String readLine()
{
try {
String line = _input.readLine();
if (line == null) {
throw new RuntimeException("IO error while reading to remote session container");
}
return line;
} catch (IOException e) {
closeConnection();
throw new RuntimeException("IO error while reading to remote session container: "+e);
}
}
protected long readLong()
{
try {
String line = _input.readLine();
if (line != null) {
try {
return Long.parseLong(line);
} catch (NumberFormatException e) {
}
}
} catch (IOException e) {
closeConnection();
throw new RuntimeException("IO error while reading to remote session container: "+e);
}
return 0;
}
public void invalidateSessions()
{
//operation not allowed
}
public Session getSession(String id)
{
ClientSession session = null;
synchronized(_lock) {
session = (ClientSession)_sessions.get(id);
if (session != null) {
if (!session.validate()) {
session = null;
}
} else {
openConnection();
writeLine("get,"+id);
flush();
String status = readLine();
if (status.equals("ok")) {
readLine();
long created = readLong();
long accessed = readLong();
session = new ClientSession(id, created, accessed);
_sessions.put(id, session);
}
}
}
return session;
}
public void removeSession(Session session)
{
synchronized(_lock) {
String id = session.getId();
_sessions.remove(id);
writeLine("invalidate,"+id);
flush();
readLine();
}
}
public Session createSession(int lifetime, int timeout)
{
synchronized(_lock) {
openConnection();
writeLine("create,"+lifetime+","+timeout);
flush();
String id = readLine();
long created = readLong();
long accessed = readLong();
System.out.println("client-create-id:"+id);
ClientSession session = new ClientSession(id, created, accessed);
_sessions.put(id, session);
System.out.println("client-create:"+session);
return session;
}
}
public Session[] getSessions()
{
synchronized(_lock) {
openConnection();
writeLine("list");
flush();
int size = (int)readLong();
Session[] sessions = new Session[size];
for(int i=0; i<size; i++) {
String id = readLine();
long created = readLong();
long accessed = readLong();
ClientSession session = (ClientSession)_sessions.get(id);
if (session == null) {
session = new ClientSession(id, created, accessed);
_sessions.put(id, session);
}
sessions[i] = session;
}
return sessions;
}
}
public class ClientSession implements Session
{
protected String _id;
protected long _created;
protected long _accessed;
public ClientSession(String id, long created, long accessed)
{
_id = id;
_created = created;
_accessed = accessed;
}
public String toString()
{
return _id;
}
public String getId()
{
return _id;
}
public void removeAttribute(String name)
{
synchronized(_lock) {
openConnection();
writeLine("dispose,"+_id+","+Conversions.URLEncode(name));
flush();
readLine();
}
}
public void setAttribute(String name, Object value)
{
synchronized(_lock) {
openConnection();
writeLine("store,"+_id+","+Conversions.URLEncode(name));
Any data = Any.create(value);
try {
Serialization.serialize(null, data, _output);
flush();
} catch (IOException e) {
closeConnection();
throw new RuntimeException("IO error while writing to remote session container: "+e);
}
readLine();
}
}
public Object getAttribute(String name)
{
synchronized(_lock) {
openConnection();
writeLine("fetch,"+_id+","+Conversions.URLEncode(name));
flush();
if (readLine().equals("ok")) {
try {
return Serialization.unserialize(null, _input);
} catch (IOException e) {
closeConnection();
throw new RuntimeException("IO error while reading from remote session container: "+e);
} catch (UnserializationException e) {
}
}
}
return null;
}
public BindingEnumeration enumeration()
{
synchronized(_lock) {
openConnection();
writeLine("names,"+_id);
flush();
if (readLine().equals("ok")) {
int size = (int)readLong();
String[] names = new String[size];
for(int i=0; i<size; i++) {
names[i] = readLine();
}
return new AttributeEnumeration(names);
}
}
return BindingEnumeration.EMPTY;
}
public long getCreationTime()
{
return _created;
}
public long getLastAccessTime()
{
return _accessed;
}
public String getCitizen()
{
synchronized(_lock) {
openConnection();
writeLine("getcitizen,"+_id);
flush();
if (readLine().equals("ok")) {
String citizen = readLine();
if (citizen.length() > 0) {
return citizen;
}
}
}
return null;
}
public void setCitizen(String citizen)
{
synchronized(_lock) {
openConnection();
writeLine("setcitizen,"+_id+","+Conversions.URLEncode(citizen));
flush();
readLine();
}
}
public void touch()
{
synchronized(_lock) {
openConnection();
writeLine("touch,"+_id);
flush();
readLine();
}
}
public void invalidate()
{
removeSession(this);
}
public boolean validate()
{
synchronized(_lock) {
openConnection();
writeLine("validate,"+_id);
flush();
return readLine().equals("valid");
}
}
public class AttributeEnumeration implements BindingEnumeration
{
protected String[] _names;
protected int _index = 0;
public AttributeEnumeration(String[] names)
{
_names = names;
}
public boolean hasMoreElements()
{
return _index < _names.length;
}
public Object nextKey()
{
return _names[_index];
}
public Object nextElement()
{
return getAttribute(_names[_index++]);
}
}
}
public class SessionCleaner extends Thread
{
protected int _checkout = 5 * 1000;
protected boolean _running = true;
public SessionCleaner(String name, int checkout)
{
super(name);
_checkout = checkout * 1000;
}
public void shutdown()
{
_running = false;
this.interrupt();
}
public void run() {
_zone.log().info("Starting...");
while(_running) {
try {
sleep(_checkout);
} catch (InterruptedException e) {
}
if (!_running) {
break;
}
synchronized(_lock) {
Enumeration enum = _sessions.elements();
while(enum.hasMoreElements()) {
ClientSession session = (ClientSession)enum.nextElement();
}
enum = null;
}
}
_zone.log().info("Shutting down...");
}
}
}