//This file is part of SECONDO. //Copyright (C) 2021, University in Hagen, Department of Computer Science, //Database Systems for New Applications. //SECONDO 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 2 of the License, or //(at your option) any later version. //SECONDO 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 SECONDO; if not, write to the Free Software //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import java.io.*; import java.net.*; import java.util.*; import org.jpl7.*; import java.nio.charset.Charset; import java.util.SortedMap; public class OptimizerServer extends Thread{ static { System.loadLibrary( "jpl" ); System.loadLibrary( "regSecondo" ); } public static native int registerSecondo(); /** shows a prompt at the console */ private static void showPrompt(){ cout.print("\n opt-server > "); } /** init prolog * register the secondo predicate * loads the optimizer prolog code */ private boolean initialize(){ // later here is to invoke the init function which // registers the Secondo(command,Result) predicate to prolog if(registerSecondo()!=0){ System.err.println("error in registering the secondo predicate "); return false; } //cout.println("registerSecondo successful"); try{ String[] plargs = {"--stack-limit=256M"}; boolean ok = JPL.init(plargs); // VTA - 18.09.2006 // I added this piece of code in order to run with newer versions // of prolog. Without this code, the libraries (e.g. lists.pl) are // not automatically loaded. It seems that something in our code // (auxiliary.pl and calloptimizer.pl) prevents them to be // automatically loaded. In order to solve this problem I added // a call to 'member(x, [x]).' so that the libraries are loaded // before running our scripts. Term[] args = new Term[2]; args[0] = new Atom("x"); args[1] = org.jpl7.Util.termArrayToList( new Term[] { new Atom("x") } ); Query q = new Query("member",args); if(!q.hasSolution()){ cout.println("error in the member call'"); return false; } args = new Term[1]; args[0] = new Atom("auxiliary"); q = new Query("consult",args); if(!q.hasSolution()){ cout.println("error in loading 'auxiliary.pl'"); return false; } args = new Term[1]; args[0] = new Atom("calloptimizer"); q = new Query("consult",args); if(!q.hasSolution()){ cout.println("error in loading 'calloptimizer.pl'"); return false; } return true; } catch(Exception e){ cout.println("Exception in initialization "+e); e.printStackTrace(); return false; } } public static boolean halt(){ try{ Query q = new Query("halt"); boolean res = command(q,null); return res; } catch(Exception e){ System.err.println(" error in shutting down the prolog engine"); e.printStackTrace(); return false; } } /** invokes a prolog predicate * all terms in variables must be contained in arguments * variables and results can be null if no result is desired */ synchronized private static boolean command(Query pl_query, Vector results){ if(pl_query==null) return false; try{ if(trace){ cout.println("execute query: "+pl_query); } String ret =""; int number =0; // the number of solutions if(results!=null) results.clear(); while(pl_query.hasMoreSolutions()){ number++; Map solution = pl_query.nextSolution(); if(results!=null){ ret = ""; if(solution.size()<=0){ results.add("yes"); } else{ Set vars = solution.keySet(); Iterator it = vars.iterator(); int varnum =0; while(it.hasNext()){ String k = it.next(); if (varnum>0) ret += " $$$ "; ret += ( k + " = " + solution.get(k)); varnum++; } results.add(ret); } } } if(number == 0){ if(trace) cout.println("no solution found for' "+pl_query.goal() +"/"+pl_query.goal().arity()); return false; } else{ // check if the used database is changed Query dbQuery = new Query("databaseName(X)"); if(dbQuery.hasMoreSolutions()){ Map sol = dbQuery.nextSolution(); String v = sol.keySet().iterator().next(); String name = ""+sol.get(v); if(!name.equals(openedDatabase)){ if(trace){ cout.println("use database "+ name); } openedDatabase=name; } } else{ // no database open openedDatabase = ""; } return true; } } catch(Exception e){ if(trace){ cout.println("exception in calling the "+pl_query.goal()+"-predicate"+e); e.printStackTrace(); } return false; } } /** analyzed the given string and extract the command and the argumentlist *
then the given command is executed * all Solutions are stored into the Vector Solution */ private synchronized static boolean execute(String command,Vector Solution){ if(Solution!=null) Solution.clear(); // clients should not have the possibility // to shut down the Optimizer-Server command = command.trim(); if(command.startsWith("halt ") || command.equals("halt")) return false; Vector TMPVars = new Vector(); if(trace){ cout.println("analyse command: "+command); } command = encode(command); try{ Query pl_query = new Query(command); if(pl_query==null){ if(trace) cout.println("error in parsing command: "+command); return false; } boolean res = command(pl_query,Solution); if(res & trace & Solution!=null){ // successful if(Solution.size()==0) cout.println("Yes"); else for(int i=0;i 1){ allret += "\n"; } Map solution = pl_query.nextSolution(); if(solution.size()!=1){ if(trace){ cout.println("Error: optimization returns more than a single binding"); } return query; } Iterator it = solution.keySet().iterator(); while(it.hasNext()){ allret += "" + solution.get(it.next()); } if(number==1){ ret1 = allret; } } if(number>1){ if(trace){ cout.println("Error: optimization returns more than one solution"); cout.println("Solutions are \n" + allret); } ret1 = ret1.trim(); if(ret1.startsWith("'") && ret1.endsWith("'")){ if(ret1.length()==2){ ret1 = ""; } else{ ret1 = ret1.substring(1,ret1.length()-1); } } return decode(ret1); } if(number==0){ if(trace) cout.println("optimization failed - no solution found"); return query; } else{ if(trace) cout.println("\n optimization-result : "+allret+"\n"); // free ret from enclosing '' allret = allret.trim(); if(allret.startsWith("'") && allret.endsWith("'")){ if(allret.length()==2){ allret = ""; } else{ allret = allret.substring(1,allret.length()-1); } } return decode(allret); } } catch(Exception e){ if(trace) { cout.println("\n Exception :"+e); e.printStackTrace(); } showPrompt(); return query; } } /** if Name is not the database currenty used, * the opened database is closed and the database * Name is opened */ private synchronized boolean useDatabase(String Name){ if(openedDatabase.equals(Name)){ return true; } Term[] arg = new Term[1]; if(!openedDatabase.equals("")){ if(trace) cout.println("close the opened database"); Query Q = new Query("secondo('close database')"); command(Q,null); } Query Q_open = new Query("secondo('open database "+Name+"')"); showPrompt(); boolean res = command(Q_open,null); openedDatabase = res?Name:""; return res; } /** a class for communicate with a client */ private class Server extends Thread{ /** creates a new Server from given socket */ public Server(Socket S){ this.S = S; //cout.println("requesting from client"); try{ // communication with client, always use UTF-8 encoding try{ in = new BufferedReader(new InputStreamReader(S.getInputStream(),"UTF-8")); } catch(UnsupportedEncodingException e){ System.err.println("Encoding UTF-8 not supported"); in = new BufferedReader(new InputStreamReader(S.getInputStream())); } try{ out = new BufferedWriter(new OutputStreamWriter(S.getOutputStream(),"UTF-8")); } catch(UnsupportedEncodingException e){ System.err.println("Encoding UTF-8 not supported"); out = new BufferedWriter(new OutputStreamWriter(S.getOutputStream())); } String First = in.readLine(); //cout.println("receive :"+First); if(First==null){ if(trace) cout.println("connection broken"); showPrompt(); running=false; return; } if(First.equals("")){ out.write("\n",0,12); out.flush(); running = true; }else{ if(trace) cout.println("protocol-error , close connection (expect: , received :"+First); showPrompt(); running = false; } }catch(Exception e){ if(trace){ cout.println("Exception occured "+e); e.printStackTrace(); } showPrompt(); running = false; } } /** disconnect this server from a client */ private void disconnect(){ // close Connection if possible try{ if(S!=null) S.close(); }catch(Exception e){ if(trace){ cout.println("Exception in closing connection "+e); e.printStackTrace(); } } OptimizerServer.Clients--; if(trace){ cout.println("\nbye client"); cout.println("number of clients is :"+ OptimizerServer.Clients); } if((OptimizerServer.Clients==0) && OptimizerServer.quitAfterDisconnect){ // OptimizerServer.halt(); // not allowed from this tread System.exit(0); } showPrompt(); } /** processes requests from clients until the client * finish the connection or an error occurs */ public void run(){ if(!running){ disconnect(); return; } boolean execFlag=false; // flags for control execute or optimize request try{ String input = in.readLine(); if(input==null){ if(trace) cout.println("connection is broken"); disconnect(); return; } while(!input.equals("")){ if(!input.equals("") && !input.equals("") ){ // protocol_error if(trace) cout.println("protocol error( expect: or , found:"+input); disconnect(); return; } //cout.println("receive "+input+" from client"); execFlag = input.equals(""); // read the database name input = in.readLine(); //cout.println("receive "+input+" from client"); if(input==null){ if(trace) cout.println("connection is broken"); disconnect(); return; } if(!input.equals("")){ // protocol_error if(trace) cout.println("protocol error( expect: , found:"+input); disconnect(); return; } String Database = in.readLine(); //cout.println("receive "+Database+" from client"); if(Database==null){ cout.println("connection is broken"); disconnect(); return; }else { Database = Database.trim(); } input = in.readLine(); //cout.println("receive "+input+" from client"); if(input==null){ if(trace) cout.println("connection is broken"); disconnect(); return; }else{ input = input.trim(); } if(!input.equals("")){ // protocol error if(trace) cout.println("protocol error( expect: , found:"+input); disconnect(); return; } input = in.readLine(); //cout.println("receive "+input+" from client"); if(input==null){ if(trace) cout.println("connection is broken"); disconnect(); return; }else{ input = input.trim(); } if(!input.equals("")){ // protocol error if(trace) cout.println("protocol error( expect: , found:"+input); disconnect(); return; } StringBuffer res = new StringBuffer(); // build the query from the next lines input = in.readLine(); //cout.println("receive "+input+" from client"); //cout.println("receive"+input); if(input==null){ if(trace) cout.println("connection is broken"); disconnect(); return; } while(!input.equals("")){ res.append(input + "\n"); input = in.readLine(); //cout.println("receive"+input); if(input==null){ if(trace) cout.println("connection is broken"); disconnect(); return; } } input = in.readLine(); //cout.println("receive "+input+" from client"); if(input==null){ cout.println("connection is broken"); disconnect(); return; } if(! (input.equals("") & !execFlag) & !(input.equals("") & execFlag)){ //protocol-error if(trace) cout.println("protocol error( expect: or , found:"+input); disconnect(); return; } String Request = res.toString().trim(); //cout.println("Request is " + Request ); Vector V = new Vector(); synchronized(SyncObj){ useDatabase(Database); if(!execFlag){ String opt = OptimizerServer.this.optimize(Request); if(!opt.equals(Request)) V.add(opt); }else{ execute(Request,V); } } showPrompt(); out.write("\n",0,9); String answer; for(int i=0;i\n",0,10); out.flush(); input = in.readLine().trim(); //cout.println("receive "+input+" from client"); if (input==null){ cout.println("connection broken"); showPrompt(); disconnect(); return; } } // while cout.println("connection ended normally"); showPrompt(); }catch(IOException e){ cout.println("error in socket-communication" + e); disconnect(); showPrompt(); return; } disconnect(); } private BufferedReader in; private BufferedWriter out; private boolean running; private Socket S; } /** creates the server process */ private boolean createServer(){ SS=null; try{ SS = new ServerSocket(PortNr); } catch(java.net.BindException be){ cout.println("BindException occured"); cout.println("check if the port "+PortNr+" is already in use"); return false; } catch(Exception e){ cout.println("unable to create a ServerSocket" + e); e.printStackTrace(); return false; } return true; } /** waits for request from clients * for each new client a new socket communicationis created */ public void run(){ cout.println("\nwaiting for requests"); showPrompt(); while(running){ try{ Socket S = SS.accept(); Clients++; if(trace){ cout.println("\na new client is connected"); cout.println("number of clients :"+Clients); showPrompt(); } (new Server(S)).start(); } catch(Exception e){ cout.println("error in communication" + e); showPrompt(); } } } private static void showUsage(){ cout.println("java -classpath .: OptimizerServer PORT [options] "); cout.println(" : jar file containing the JPL (prolog) API"); cout.println("PORT : port for the server"); cout.println("[Options can be :"); cout.println(" -autoquit : exits the server if the last client disconnects"); cout.println(" -trace_methods : enables tracing of method calls (for debugging)"); cout.println(" -trace_instructions : enables tracing of instructions (for debugging)"); cout.println(" -trace_commands : enables tracing of input/output"); cout.println(" -encoding enc : switch the output encoding"); } /** creates a new server object * process inputs from the user * available commands are * client : prints out the number of connected clients * quit : shuts down the server */ public static void main(String[] args){ cout = System.out; if(args.length<1){ showUsage(); System.exit(1); } // process options Runtime rt = Runtime.getRuntime(); int pos = 1; String console_enc = "utf-8"; // standard while(pos=args.length){ showUsage(); System.exit(1); } console_enc = args[pos+1]; pos++; pos++; } else if(args[pos].equals("-trace_commands")){ trace=true; pos++; } else { cout.println("unknown option " + args[pos]); showUsage(); System.exit(1); } } if(!console_enc.equals("utf-8")){ SortedMap available = Charset.availableCharsets(); if(!available.containsKey(console_enc)){ cout.println("encoding " + console_enc + " unknown"); cout.println(" Available encodinga are : "); cout.println(available.keySet()); System.exit(1); } try { cout = new PrintStream(System.out, true, console_enc); } catch(Exception e){ cout = System.out; cout.println("Problem in changing output encoding, use utf-8"); e.printStackTrace(); } } cout.println("\n\n"); printLicence(cout); cout.println("\n"); String arg = args[0]; OptimizerServer OS = new OptimizerServer(); try{ int P = java.lang.Integer.parseInt(arg); if(P<=0){ System.err.println("the Portnumber must be greater then zero"); System.exit(1); } OS.PortNr=P; }catch(Exception e){ System.err.println("the Portnumber must be an integer"); System.exit(1); } try{ Class.forName("org.jpl7.fli.Prolog"); // ensure to load the jpl library } catch(Exception e){ System.err.println("loading prolog class failed"); System.exit(1); } if(! OS.initialize()){ cout.println("initialization failed"); System.exit(1); } if(!OS.createServer()){ cout.println("creating Server failed"); System.exit(1); } OS.running = true; OS.start(); try{ BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); cout.print("optserver >"); String command = ""; Vector ResVector = new Vector(); while(!command.equals("quit")){ command = in.readLine(); if(command==null){ command = ""; continue; } command = command.trim(); if(command.equals("clients")){ cout.println("Number of Clients: "+ Clients); }else if(command.equals("quit")){ if( Clients > 0){ cout.print("clients exists ! shutdown anyway (y/n) >"); String answer = in.readLine().trim().toLowerCase(); if(!answer.startsWith("y")) command=""; } } else if(command.equals("trace-on")){ trace=true; cout.println("tracing is activated"); } else if(command.equals("trace-off")){ trace=false; cout.println("tracing is deactivated"); } else if(command.equals("help") | command.equals("?") ){ cout.println("quit : quits the server "); cout.println("clients : prints out the number of connected clients"); cout.println("trace-on : prints out messages about command, optimized command, open database"); cout.println("trace-off : disable messages"); } else if(command.startsWith("exec")){ String cmdline = command.substring(4,command.length()).trim(); execute(cmdline,ResVector); }else if(!command.equals("")){ cout.println("unknow command, try help show a list of valid commands"); } if(!command.equals("quit")) showPrompt(); } OS.running = false; }catch(Exception e){ OS.running = false; cout.println("error in reading commands"); if(trace) e.printStackTrace(); } try{ Query q = new Query("halt"); command(q,null); } catch(Exception e){ System.err.println(" error in shutting down the prolog engine"); } } /** Prints out the licence information of this software **/ public static void printLicence(PrintStream out){ cout.println(" Copyright (C) 2021, University in Hagen, "); cout.println(" Faculty of Mathematics and Computer Science, "); cout.println(" Database Systems for New Applications. \n"); cout.println(" This is free software; see the source for copying conditions."); cout.println(" There is NO warranty; not even for MERCHANTABILITY or FITNESS "); cout.println(" FOR A PARTICULAR PURPOSE."); } /** Converts a string into OS_pl_encoding **/ private static String encode(String src){ if(OS_pl_encoding==null){ return src; } try{ byte[] encodedBytes = src.getBytes(OS_pl_encoding); return new String(encodedBytes, "UTF-8"); } catch(Exception e){ System.err.println("Used encoding not supported\n" + e); return src; } } /** Converts a string from OS_pl_encoding **/ private static String decode(String src){ if(OS_pl_encoding==null){ return src; } try{ byte[] encodedBytes = src.getBytes("UTF-8"); return new String(encodedBytes, OS_pl_encoding); } catch(Exception e){ System.err.println("Used encoding not supported\n" + e); return src; } } private static String OS_pl_encoding = null; private boolean optimizer_loaded = false; private int PortNr = 1235; private static int Clients = 0; private static boolean quitAfterDisconnect = false; private ServerSocket SS; private boolean running; private static String openedDatabase =""; private static boolean trace = true; private static Object SyncObj = new Object(); private static PrintStream cout; }