package abstrasy.libraries.io;
import abstrasy.Bivalence;
import abstrasy.Interpreter;
import abstrasy.Node;
import abstrasy.SELF;
import abstrasy.externals.AExtCachable;
import abstrasy.externals.AExtClonable;
import abstrasy.externals.OptAccess;
import abstrasy.externals.OptAccessList;
import abstrasy.externals.OptAccessProvider;
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.ORef;
import abstrasy.interpreter.StdErrors;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
/**
* Abstrasy Interpreter
*
* Copyright : Copyright (c) 2006-2012, Luc Bruninx.
*
* Concédée sous licence EUPL, version 1.1 uniquement (la «Licence»).
*
* Vous ne pouvez utiliser la présente oeuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://www.osor.eu/eupl
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous
* la Licence est distribué "en l’état", SANS GARANTIES OU CONDITIONS QUELLES
* QU’ELLES SOIENT, expresses ou implicites.
*
* Consultez la Licence pour les autorisations et les restrictions
* linguistiques spécifiques relevant de la Licence.
*
*
* @author Luc Bruninx
* @version 1.0
*/
public class External_Server extends OptAccessProvider implements AExtClonable, AExtCachable {
boolean inUse = false;
int soTimeOut=0;
String protocol=null;
int tcpPort = 0;
int maxConnexion=0;
InetAddress maskIp = null;
ServerSocket serverSocket = null;
private OptAccess oa_socket_timeout = new OptAccess("socket-timeout") {
public Node getNode() {
return new Node(soTimeOut);
}
public void setNode(Node optNode) throws Exception {
optNode.requireNodeType(Node.TYPE_NUMBER);
soTimeOut = Math.min(0, (int) optNode.getNumber());
}
};
private OptAccess oa_Protocol = new OptAccess("Protocol") {
public Node getNode() {
return new Node(protocol);
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_Port = new OptAccess("Port") {
public Node getNode() {
return new Node(serverSocket!=null ? serverSocket.getLocalPort() : tcpPort);
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_Max_Connections = new OptAccess("Max-Connections") {
public Node getNode() {
return new Node(maxConnexion);
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_Bind_IP = new OptAccess("Bind-IP") {
public Node getNode() {
return maskIp!=null ? new Node(maskIp.getHostAddress()):Node.NOTHING_FINAL_NODE;
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_Host = new OptAccess("Host") {
public Node getNode() {
return serverSocket!=null ? new Node(serverSocket.getInetAddress().getHostName()):Node.NOTHING_FINAL_NODE;
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess oa_IP = new OptAccess("IP") {
public Node getNode() {
return serverSocket!=null ? new Node(serverSocket.getInetAddress().getHostAddress()):Node.NOTHING_FINAL_NODE;
}
public void setNode(Node optNode) throws Exception {
throw new InterpreterException(StdErrors.Illegal_access_to_final_value);
}
};
private OptAccess[] DEFAULT_OPTIONS = new OptAccess[] { oa_socket_timeout, oa_Protocol, oa_Port,oa_Max_Connections,oa_Bind_IP,oa_Host,oa_IP};
public External_Server() {
this.setOptAccessList(new OptAccessList(DEFAULT_OPTIONS));
}
/*
* ----------------------------------------------------------------------------
*/
public Node external_mutator_open_tcp(Node startAt) throws Exception {
/*
* formes:
* (:open-tcp!)
* (:open-tcp! port)
* (:open-tcp! port max-connexion)
* (:open-tcp! port max-connexion "ListenAddr") # l'ListenAddr est l'ip ou l'adresse où le serveur écoute.
*/
if (inUse)
throw new InterpreterException(StdErrors.extend(StdErrors.Already_used, "Server is already runing"));
startAt.isGoodArgsCnt(1, 2, 3, 4);
SELF.require_SELF_mutable();
tcpPort=0;
maxConnexion = 0;
maskIp = null;
switch (startAt.size()) {
case 4:
maskIp = InetAddress.getByName(startAt.getSubNode(3, Node.TYPE_STRING).getString());
case 3:
maxConnexion = (int) startAt.getSubNode(2, Node.TYPE_NUMBER).getNumber();
case 2:
tcpPort = (int) startAt.getSubNode(1, Node.TYPE_NUMBER).getNumber();
break;
default:
tcpPort=0;
maxConnexion=0;
maskIp=null;
}
if (tcpPort < 0 || tcpPort > 65535)
throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "" + tcpPort));
serverSocket = new ServerSocket(tcpPort, maxConnexion, maskIp);
soTimeOut=0;
protocol="tcp";
/*
* On place un hook en cas de problème ou d'oubli...
*/
putHook();
inUse = true;
return null;
}
public Node external_mutator_close(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
internal_close();
/*
* On retire le hook puisque les fichiers sont fermé correctement.
*/
removeHook();
return null;
}
public Node external_is_opened(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(inUse ? Node.TRUE: Node.FALSE);
}
public Node external_mutator_accept(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
SELF.require_SELF_mutable();
serverSocket.setSoTimeout((int)oa_socket_timeout.getNode().getNumber());
Socket sock=null;
try{
sock = serverSocket.accept();
}
catch(SocketTimeoutException soe){
sock=null;
}
// si le time-out est atteint, on retourne NOTHING
if(sock==null)
return Node.createNothing();
// si une connexion est ouverte, on retourne un File
External_File file = new External_File();
file.setSocket(sock);
file.setInputStream(sock.getInputStream());
file.setOutputStream(sock.getOutputStream());
// le file est automatiquement marqué comme ouvert.
file.setTcpPort(sock.getPort());
file.setTcpHost(sock.getInetAddress().getHostName());
file.setTcpIP(sock.getInetAddress().getHostAddress());
file.setProtocol(protocol);
file.setOptAccessList(new OptAccessList(file.getSOCKET_OPTIONS()));
file.getOptAccessList().set(file.getOa_Host());
file.getOptAccessList().set(file.getOa_IP());
file.getOptAccessList().set(file.getOa_Port());
return create_File_adp(file);
}
/*
* ----------------------------------------------------------------------------
*
*
*
*/
private Node create_File_adp(External_File efile) throws Exception {
return Node.createExternal(efile);
}
public void internal_close() throws Exception {
if (serverSocket != null)
serverSocket.close();
serverSocket = null;
soTimeOut=0;
protocol=null;
tcpPort=0;
maxConnexion=0;
maskIp=null;
inUse = false;
}
/**
* Clé du hook...
*/
private Object myHookKey;
/**
* Ajoute un hook en cas où on oublierait de fermer les fichiers et les flux...
*
* Ce hook sera automatiquement exécuté lors de l'arrêt de l'interpréteur, à moins
* que le fichier soit refermé manuellement. De cette manière, les fichiers devraient
* conserver un état stable.
*
*/
private void putHook() {
final WeakReference<External_Server> myself = new WeakReference<External_Server>(this);
Runnable hook = new Runnable() {
public void run() {
External_Server me = myself.get();
if (me != null) {
if (Interpreter.isDebugMode()) {
Interpreter.Log("Io:Server Hook : close " + me + "...");
}
try {
me.internal_close();
}
catch (Exception e) {
Interpreter.Log(" -> Exception: " + e.toString());
}
}
}
};
myHookKey = Interpreter.supervisor_PUTHOOK(hook);
}
/**
* On retire le hook...
*/
private void removeHook() {
Interpreter.supervisor_REMOVEHOOK(myHookKey);
myHookKey = null;
}
@Override
public Object clone_my_self(Bivalence bival) {
return null;
}
/*
* ----------------------------------------------------------------------------
*
* Optimisation par cache d'instanciantion (mars 2012) rev 1.0-6321.0
*
* ----------------------------------------------------------------------------
*/
private static ORef _optim_symbols_cache_ = new ORef();
@Override
public Object getSymbolsCache() {
return _optim_symbols_cache_.getRef();
}
@Override
public void setSymbolsCache(Object cache) {
_optim_symbols_cache_.setRef(cache);
}
}