From c8e2100eb55290ab30b27bdbf92b92e38ac7f260 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ale=C5=A1=20K=C5=99enek?= Date: Wed, 3 Jun 2009 07:21:01 +0000 Subject: [PATCH] query and notification API --- org.glite.lb.client-java/Makefile | 1 + .../nbproject/project.properties | 1 + .../src/org/glite/lb/ILProtoReceiver.java | 138 ++++++ org.glite.lb.client-java/src/org/glite/lb/Job.java | 168 +++++++ .../src/org/glite/lb/LBCredentials.java | 63 +++ .../src/org/glite/lb/LBException.java | 6 +- .../src/org/glite/lb/NotifParser.java | 128 +++++ .../src/org/glite/lb/Notification.java | 241 ++++++++++ .../src/org/glite/lb/NotificationExample.java | 95 ++++ .../src/org/glite/lb/ServerConnection.java | 521 +++++++++++++++++++++ .../src/org/glite/lb/ServerConnectionExample.java | 249 ++++++++++ 11 files changed, 1609 insertions(+), 2 deletions(-) create mode 100644 org.glite.lb.client-java/src/org/glite/lb/ILProtoReceiver.java create mode 100644 org.glite.lb.client-java/src/org/glite/lb/Job.java create mode 100644 org.glite.lb.client-java/src/org/glite/lb/LBCredentials.java create mode 100644 org.glite.lb.client-java/src/org/glite/lb/NotifParser.java create mode 100644 org.glite.lb.client-java/src/org/glite/lb/Notification.java create mode 100644 org.glite.lb.client-java/src/org/glite/lb/NotificationExample.java create mode 100644 org.glite.lb.client-java/src/org/glite/lb/ServerConnection.java create mode 100644 org.glite.lb.client-java/src/org/glite/lb/ServerConnectionExample.java diff --git a/org.glite.lb.client-java/Makefile b/org.glite.lb.client-java/Makefile index 69de860..da86271 100644 --- a/org.glite.lb.client-java/Makefile +++ b/org.glite.lb.client-java/Makefile @@ -11,6 +11,7 @@ jglobus_jar := $(shell ls ${jglobus_prefix}/lib/cog-jglobus-*.jar | sort | tail all compile: JAVA_HOME=${jdk_prefix} \ ${ant_prefix}/bin/ant -Dno.deps=yes -DstageDir=${stagedir} \ + -Dfile.reference.commons-lang.jar=${commons-lang_jar} \ -Dreference.jobid-api-java.jar=${stagedir}/share/java/jobid-api-java.jar \ -Dreference.trustmanager.jar=${trustmanager_prefix}/share/java/glite-security-trustmanager.jar \ -Daxis.classpath=`ls ${axis_prefix}/lib/*.jar | tr '\012' :` diff --git a/org.glite.lb.client-java/nbproject/project.properties b/org.glite.lb.client-java/nbproject/project.properties index feaebae..1ebbb91 100755 --- a/org.glite.lb.client-java/nbproject/project.properties +++ b/org.glite.lb.client-java/nbproject/project.properties @@ -21,6 +21,7 @@ excludes= includes=** jar.compress=false javac.classpath=\ + ${file.reference.commons-lang.jar}:\ ${axis.classpath}:\ ${reference.jobid-api-java.jar}:\ ${reference.trustmanager.jar}:\ diff --git a/org.glite.lb.client-java/src/org/glite/lb/ILProtoReceiver.java b/org.glite.lb.client-java/src/org/glite/lb/ILProtoReceiver.java new file mode 100644 index 0000000..0c775d0 --- /dev/null +++ b/org.glite.lb.client-java/src/org/glite/lb/ILProtoReceiver.java @@ -0,0 +1,138 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.glite.lb; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * this class handles communication with LB server (reads messages it sends) + * + * @author Kopac + */ +public class ILProtoReceiver { + + private Socket socket = null; + private InputStream inStream = null; + private OutputStream outStream = null; + private static final String magicWord = "6 michal"; + + /** + * construcor initializes the class' socket, inStream and outStream attributes + * + * @param socket an SSLSocket + * @throws java.io.IOException + */ + public ILProtoReceiver(Socket socket) throws IOException { + this.socket = socket; + inStream = this.socket.getInputStream(); + outStream = this.socket.getOutputStream(); + } + + /** + * this method reads from the inpuStream of the provided socket, checks for + * the magic word and returns relevant info + * + * @return a String containing third line of the inputStream data, without + * the info about its length + * @throws IOException + */ + public String receiveMessage() throws IOException{ + byte[] b = new byte[17]; + int i = 0; + //read in and convert size of the message + if(inStream.read(b, 0, 17) == -1) { + return null; + } else { + String test = new String(b); + test.trim(); + int length = Integer.parseInt(test); + byte[] notification = new byte[length]; + //read in the rest of the message + int j = 0; + while(i != length || j == -1) { + j = inStream.read(notification, i, length); + i=i+j; + } + String retString = checkWord(notification); + if(retString == null) return null; + else + //return + return retString.split(" ", 2)[1]; + } + } + + /** + * private method that checks, if the magic word is present in the notification + * + * @param notification a notification without the line specifying its length + * @return null, if the word is not there, the last line of the notification, + * again without its length specification, otherwise + */ + private String checkWord(byte[] notification) { + int i = 0; + while(notification[i] != '\n') { + i++; + } + String word = new String(notification, 0, i+1); + word.trim(); + if(!word.equals(magicWord)) { + return null; + } else { + return new String(notification, i+1, notification.length - i + 1); + } + } + + /** + * this method encodes and sends a reply to the interlogger via the socket's + * outputStream + * + * @param errCode errCode of the calling + * @param minErrCode minimum available errcode + * @param message message for the interlogger - could be any String + * @throws IOException + */ + public void sendReply(int errCode, int minErrCode, String message) throws IOException { + byte[] errByte = (new String()+errCode).getBytes(); + byte[] minErrByte = (new String()+minErrCode).getBytes(); + byte[] msgByte = message.getBytes(); + int length = errByte.length + minErrByte.length + msgByte.length; + byte[] lengthByte = (new String()+length).getBytes(); + int numberOfSpaces = 17 - lengthByte.length; + byte[] returnByte = new byte[length+17]; + int i = 0; + while(i < numberOfSpaces-1) { + returnByte[i] = ' '; + i++; + } + returnByte = putByte(returnByte, lengthByte, numberOfSpaces); + returnByte[16] = '\n'; + returnByte = putByte(returnByte, errByte, 17); + returnByte = putByte(returnByte, minErrByte, 16 + errByte.length); + returnByte = putByte(returnByte, msgByte, 16 + errByte.length + minErrByte.length); + outStream.write(returnByte); + } + + /** + * appends a byte array to the end of an existing byte array + * + * @param arrayToFill array to be filled + * @param filler array to be appended + * @param start starting offset of the first array, from which the second + * array should be appended + * @return the resulting byte array + */ + private byte[] putByte(byte[] arrayToFill, byte[] filler, int start) { + for(int i = start; i < filler.length + start; i++) { + int j = 0; + arrayToFill[i] = filler[j]; + j++; + } + return arrayToFill; + } +} diff --git a/org.glite.lb.client-java/src/org/glite/lb/Job.java b/org.glite.lb.client-java/src/org/glite/lb/Job.java new file mode 100644 index 0000000..e33365c --- /dev/null +++ b/org.glite.lb.client-java/src/org/glite/lb/Job.java @@ -0,0 +1,168 @@ + +package org.glite.lb; + +import java.util.ArrayList; +import java.util.List; +import org.glite.jobid.Jobid; +import org.glite.lb.Event; +import org.glite.wsdl.types.lb.JobFlagsValue; +import org.glite.wsdl.types.lb.JobStatus; +import org.glite.wsdl.types.lb.QueryAttr; +import org.glite.wsdl.types.lb.QueryConditions; +import org.glite.wsdl.types.lb.QueryOp; +import org.glite.wsdl.types.lb.QueryRecValue; +import org.glite.wsdl.types.lb.QueryRecord; + +/** + * Class encapsulating the job info stored in the L&B database. + * + * This class is the primary interface for getting information about + * job stored in the L&B database. It is constructed from known job + * id, which uniquely identifies the job as well as the bookkeeping + * server where the job data is stored. The Job class provides methods + * for obtaining the data from the bookkeeping server. + * + * @author Tomas Kramec, 207545@mail.muni.cz + */ +public class Job { + + private Jobid jobId; + private ServerConnection serverConnection; + + /** + * Constructor initializes the job as empty, not representing anything. + */ + public Job() { + } + + /** + * Constructor from job id and bookkeeping server connection. + * Initializes the job to obtain information for the given job id. + * + * @param jobId The job id of the job this object wil represent. + * @param serverConnection ServerConnection object providing methods + * for obtaining the data from the bookkeeping server. + */ + public Job(Jobid jobId, ServerConnection serverConnection) { + if (jobId == null) throw new IllegalArgumentException("jobId cannot be null"); + if (serverConnection == null) throw new IllegalArgumentException("server cannot be null"); + + this.jobId = jobId; + this.serverConnection = serverConnection; + } + + /** + * Constructor from job id and bookkeeping server connection. + * Initializes the job to obtain information for the given job id. + * + * @param jobId The job id of the job this object wil represent. + * @param serverConnection ServerConnection object providing methods + * for obtaining the data from the bookkeeping server. + */ + public Job(String jobId, ServerConnection serverConnection) { + this(new Jobid(jobId), serverConnection); + } + + /** + * Gets this job ID. + * + * @return jobId + */ + public Jobid getJobId() { + return jobId; + } + + /** + * Sets the jobId to this job. + * + * @param jobId + */ + public void setJobId(Jobid jobId) { + if (jobId == null) throw new IllegalArgumentException("jobId"); + + this.jobId = jobId; + } + + /** + * Gets server connection. + * + * @return serverConnection + */ + public ServerConnection getServer() { + return serverConnection; + } + + /** + * Sets server connection instance to this job. + * + * @param serverConnection + */ + public void setServer(ServerConnection serverConnection) { + if (serverConnection == null) throw new IllegalArgumentException("server"); + + this.serverConnection = serverConnection; + } + + + /** + * Return job status. + * Obtains the job status (as JobStatus) from the bookkeeping server. + * + * @param flags Specify details of the query. Determines which status + * fields are actually retrieved. + * Possible values:
    + *
  1. CLASSADS - Include the job description in the query result.
  2. + *
  3. CHILDREN - Include the list of subjob id's in the query result.
  4. + *
  5. CHILDSTAT - Apply the flags recursively to subjobs.
  6. + *
  7. CHILDHIST_FAST - Include partially complete histogram of child job states.
  8. + *
  9. CHILDHIST_THOROUGH - Include full and up-to date histogram of child job states.
  10. + *
+ * + * @return Status of the job. + * @throws LBException If some communication or server problem occurs. + */ + public JobStatus getStatus(JobFlagsValue[] flags) throws LBException { + if (serverConnection == null) + throw new IllegalStateException("serverConnection is null, please set it"); + if (jobId == null) + throw new IllegalStateException("jobId is null, please set it"); + try { + return serverConnection.jobState(jobId.toString(), flags); + } catch (LBException ex) { + throw new LBException(ex); + } + } + + /** + * Return all events corresponding to this job. + * Obtains all events corresponding to the job that are stored + * in the bookkeeping server database. + *

+ * Default value for logging level is SYSTEM. If needed, it can be changed + * by calling setEventLoggingLevel(Level loggingLevel) method + * on the serverConnection attribute of this class. + *

+ * + * @return events List of events (of type Event). + * @throws LBException If some communication or server problem occurs. + */ + public List getEvents() throws LBException { + if (serverConnection == null) + throw new IllegalStateException("serverConnection is null, please set it"); + if (jobId == null) + throw new IllegalStateException("jobId is null, please set it"); + + QueryRecValue jobIdValue = new QueryRecValue(null, jobId.toString(), null); + QueryRecord[] jobIdRec = new QueryRecord[] {new QueryRecord(QueryOp.EQUAL, jobIdValue, null)}; + QueryConditions[] query = new QueryConditions[]{new QueryConditions(QueryAttr.JOBID, null, null, jobIdRec)}; + QueryRecValue levelValue = new QueryRecValue(serverConnection.getEventLoggingLevel().getInt()+1, null, null); + QueryRecord[] levelRec = new QueryRecord[] {new QueryRecord(QueryOp.LESS, levelValue, null)}; + QueryConditions[] queryEvent = new QueryConditions[]{new QueryConditions(QueryAttr.LEVEL, null, null, levelRec)}; + List queryList = new ArrayList(); + queryList.add(query[0]); + List queryEventList = new ArrayList(); + queryEventList.add(queryEvent[0]); + + return serverConnection.queryEvents(queryList, queryEventList); + } +} diff --git a/org.glite.lb.client-java/src/org/glite/lb/LBCredentials.java b/org.glite.lb.client-java/src/org/glite/lb/LBCredentials.java new file mode 100644 index 0000000..448365b --- /dev/null +++ b/org.glite.lb.client-java/src/org/glite/lb/LBCredentials.java @@ -0,0 +1,63 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.glite.lb; + +import java.net.MalformedURLException; +import java.net.URL; +import javax.xml.rpc.ServiceException; +import org.apache.axis.AxisProperties; +import org.glite.wsdl.services.lb.LoggingAndBookkeepingLocator; +import org.glite.wsdl.services.lb.LoggingAndBookkeepingPortType; + +/** + * + */ +public class LBCredentials { + + public LBCredentials(String proxy, String caFiles) { + if (proxy == null) throw new IllegalArgumentException("Proxy cannot be null"); + if (caFiles == null) throw new IllegalArgumentException("caFiles cannot be null"); + + System.setProperty(org.glite.security.trustmanager.ContextWrapper.CREDENTIALS_PROXY_FILE, proxy); + System.setProperty(org.glite.security.trustmanager.ContextWrapper.CA_FILES, caFiles); + System.setProperty(org.glite.security.trustmanager.ContextWrapper.SSL_PROTOCOL, "SSLv3"); + AxisProperties.setProperty("axis.socketSecureFactory","org.glite.security.trustmanager.axis.AXISSocketFactory"); + } + + public LBCredentials(String userCert, String userKey, String userPass, String caFiles) { + if (userCert==null || userKey==null || userPass==null || caFiles==null) + throw new IllegalArgumentException("One of the parameters was null"); + + System.setProperty(org.glite.security.trustmanager.ContextWrapper.CREDENTIALS_CERT_FILE,userCert); + System.setProperty(org.glite.security.trustmanager.ContextWrapper.CREDENTIALS_KEY_FILE,userKey); + System.setProperty(org.glite.security.trustmanager.ContextWrapper.CREDENTIALS_KEY_PASSWD,userPass); + System.setProperty(org.glite.security.trustmanager.ContextWrapper.CA_FILES, caFiles); + System.setProperty(org.glite.security.trustmanager.ContextWrapper.SSL_PROTOCOL, "SSLv3"); + AxisProperties.setProperty("axis.socketSecureFactory","org.glite.security.trustmanager.axis.AXISSocketFactory"); + } + + protected LoggingAndBookkeepingPortType getStub(String server) throws LBException { + if (server == null) + throw new IllegalArgumentException("Server cannot be null"); + try { + URL queryServerAddress = new URL(server); + int port = queryServerAddress.getPort(); + if (port < 1 || port > 65535) { + throw new IllegalArgumentException("port"); + } + if (!queryServerAddress.getProtocol().equals("https")) { + throw new IllegalArgumentException("wrong protocol"); + } + LoggingAndBookkeepingLocator loc = new LoggingAndBookkeepingLocator(); + return loc.getLoggingAndBookkeeping(queryServerAddress); + } catch (ServiceException ex) { + throw new LBException(ex); + } catch (MalformedURLException ex) { + throw new LBException(ex); + } + } + +} diff --git a/org.glite.lb.client-java/src/org/glite/lb/LBException.java b/org.glite.lb.client-java/src/org/glite/lb/LBException.java index 62786aa..00ef3fb 100644 --- a/org.glite.lb.client-java/src/org/glite/lb/LBException.java +++ b/org.glite.lb.client-java/src/org/glite/lb/LBException.java @@ -1,7 +1,9 @@ package org.glite.lb; public class LBException extends Exception { - public LBException(Throwable e) { + + public LBException(Throwable e) { super(e); - } + } + } diff --git a/org.glite.lb.client-java/src/org/glite/lb/NotifParser.java b/org.glite.lb.client-java/src/org/glite/lb/NotifParser.java new file mode 100644 index 0000000..e18050f --- /dev/null +++ b/org.glite.lb.client-java/src/org/glite/lb/NotifParser.java @@ -0,0 +1,128 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.glite.lb; + +import java.io.IOException; +import java.io.StringReader; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import org.apache.commons.lang.StringEscapeUtils; +import org.glite.wsdl.types.lb.JobStatus; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * this class parses the received message into a readable format + * + * @author Kopac + */ +public class NotifParser { + + Document doc = null; + String header = null; + + + /** + * constructor takes a notification in String format and parses it into a String + * containing the header and an XML doc + * + * @param notif the string with notification in it + * @throws javax.xml.parsers.ParserConfigurationException + * @throws org.xml.sax.SAXException + * @throws java.io.IOException + */ + public NotifParser(String notif) throws ParserConfigurationException, SAXException, IOException { + String[] splitString = notif.split("DG.NOTIFICATION.JOBSTAT=\"", 2); + header = splitString[0]; + doc = createXML(splitString[1]); + } + + /** + * this method reads through an XML document using XPath expressions and + * fills an instance of JobStatus, which it then returns + * this is done using automatically generated code + * + * @param notification an array of bytes containing the raw notification + * @return a Jobstatus instance + */ + public JobStatus getJobInfo() + throws ParserConfigurationException, SAXException, IOException { + JobStatus status = new JobStatus(); + //TODO: insert automated code creation + status.setJobId(evaluateXPath("//jobId").item(0).getTextContent()); + status.setOwner(evaluateXPath("//owner").item(0).getTextContent()); + return status; + } + + /** + * this method returns id of the notification + * + * @return notif id + */ + public String getNotifId() { + String halfHeader = header.split("DG.NOTIFICATION.NOTIFID=\"")[1]; + return halfHeader.substring(0, halfHeader.indexOf("\"")); + } + + /** + * a method for handling xpath queries + * + * @param xpathString xpath expression + * @return the result nodelist + */ + private NodeList evaluateXPath(String xpathString) { + try { + XPathFactory xfactory = XPathFactory.newInstance(); + XPath xpath = xfactory.newXPath(); + XPathExpression expr = xpath.compile(xpathString); + return (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException ex) { + ex.printStackTrace(); + return null; + } + } + + /** + * this method creates an XML document out of a provided String + * note that the string has to be a valid escaped XML document representation, + * otherwise the process will fail + * + * @param body a String containing an XML document + * @return an XML document in the Document format + * @throws javax.xml.parsers.ParserConfigurationException + * @throws org.xml.sax.SAXException + * @throws java.io.IOException + */ + private Document createXML(String body) throws ParserConfigurationException, SAXException, IOException { + String removed = body.substring(0, body.length()-1); + String parsed = StringEscapeUtils.unescapeXml(removed); + //this code removes hexadecimal references from the string (couldn't find + //a suitable parser for this) + StringBuilder build = new StringBuilder(parsed); + int j = build.indexOf("%"); + while(j > 0) { + if(build.charAt(j-1) == '>') { + build.delete(j, j+3); + if(build.charAt(j) == '<') { + j = build.indexOf("%"); + } + } else { + j = build.indexOf("%", j+1); + } + } + parsed = build.toString(); + //ends here + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + return factory.newDocumentBuilder().parse(new InputSource(new StringReader(parsed))); + } +} diff --git a/org.glite.lb.client-java/src/org/glite/lb/Notification.java b/org.glite.lb.client-java/src/org/glite/lb/Notification.java new file mode 100644 index 0000000..e25ae3e --- /dev/null +++ b/org.glite.lb.client-java/src/org/glite/lb/Notification.java @@ -0,0 +1,241 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.glite.lb; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.rmi.RemoteException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.Calendar; +import java.util.Date; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.rpc.holders.CalendarHolder; +import org.glite.jobid.Jobid; +import org.glite.lb.SSL; +import org.glite.wsdl.services.lb.LoggingAndBookkeepingPortType; +import org.glite.wsdl.types.lb.JobFlagsValue; +import org.glite.wsdl.types.lb.JobStatus; +import org.glite.wsdl.types.lb.QueryConditions; +import org.xml.sax.SAXException; + +/** + * This class handles all communication comming from the client toward the server. + * it uses methods generated from .wsdl description files. + * note that each instance of this class is dedicated to a single port. Different + * port means a new instance has to be created + * + * @author Kopac + */ +public class Notification { + + private int port = 0; + private Socket socket = null; + private static String keyStore; + private String notifId; + private LoggingAndBookkeepingPortType stub; + private LBCredentials lbCredent; + + /** + * constructor sets the local port number + * + * @param port number of the local port the notification is bound to + * @param lbCredent instance of class that handles SSL authentication + */ + public Notification(int port, LBCredentials lbCredent) { + this.port = port; + this.lbCredent = lbCredent; + } + + /** + * a private method used to create a unique ID for a new notification + * + * @param host hostname + * @return String containing the unique ID + */ + private String makeId(String host) { + StringBuilder returnString = new StringBuilder(); + returnString.append(host); + Jobid jobid = new Jobid(returnString.toString(), port); + returnString.append("/NOTIF:"); + returnString.append(jobid.getUnique()); + return returnString.toString(); + } + + /** + * returns ID of the latest received notification + * + * @return notifID + */ + public String getNotifid() { + return notifId; + } + + /** + * private method used to recover LB server address from a notif ID + * + * @param notifId notif ID + * @return server address + */ + private String getServer(String notifId) { + StringBuilder ret = new StringBuilder(notifId.split("/")[2]); + char[] ch = new char[]{ret.charAt(ret.length()-1)}; + int i = Integer.parseInt(new String(ch)) + 3; + ret.replace(ret.length()-1, ret.length()-1, new String()+i); + ret.insert(0, "https://"); + return ret.toString(); + } + + /** + * this method sends a request to the server, to create a new notification. + * it's not necessary to provide all the options for this calling, thus + * some of the parameters can be null. in that case, the option they correspond to + * is not used. + * + * @param server a String containing the server address (e.g. https://harad.ics.muni.cz:9553). + * can't be null + * @param conditions an array of QueryConditions, may contain all the possible + * conditions for the new notification. can't be null + * @param flags an array of JobFlagsValue, may contain all the possible flags + * and their values for the new notification. + * @param date a Date containing the desired time, the notification will be valid for + * note that this option can only be used to shorten the validity span, as the server + * checks it and sets the final expiration date to a constant max, should + * the provided Date exceed it. may be null + * @return a Date holding info on how long the new notification will + * be valid for + * @throws LBException + */ + public Date newNotif(String server, QueryConditions[] conditions, JobFlagsValue[] flags, Date date) throws LBException { + try { + CalendarHolder calendarHolder = new CalendarHolder(Calendar.getInstance()); + if (date != null) { + calendarHolder.value.setTime(date); + } else { + calendarHolder.value.setTime(new Date(System.currentTimeMillis() + 86400000)); + } + stub = lbCredent.getStub(server); + String addr = "0.0.0.0:" + port; + String id = makeId(server); + stub.notifNew(id, addr, conditions, flags, calendarHolder); + notifId = id; + return calendarHolder.value.getTime(); + } catch (RemoteException ex) { + throw new LBException(ex); + } + } + + /** + * this method drops an existing notification, removing it completely + * + * @param notifId id of the notification to be dropped + * @throws LBException + */ + public void drop(String notifId) throws LBException { + try { + stub = lbCredent.getStub(getServer(notifId)); + stub.notifDrop(notifId); + } catch (RemoteException ex) { + throw new LBException(ex); + } + } + + /** + * this method is used to extend the validity of an existing notification + * + * @param notifId id of the notification to be refreshed + * @param date information about the desired validity duration of the notification + * in Date format. may be null (in this case, the maximum possible duration is used). + * @throws LBException + */ + public void refresh(String notifId, Date date) throws LBException { + try { + stub = lbCredent.getStub(getServer(notifId)); + CalendarHolder holder = new CalendarHolder(Calendar.getInstance()); + if (date != null) { + holder.value.setTime(date); + } else { + holder.value.setTime(new Date(System.currentTimeMillis() + 86400000)); + } + stub.notifRefresh(notifId, holder); + } catch (RemoteException ex) { + throw new LBException(ex); + } + } + + /** + * this method is used to bind an existing notification to a different local port + * than previously declared + * + * @param notifId id of th notification + * @param date optional attribute, can be used to refresh the notification + * @return length of the validity duration of the notification in Date format + * @throws LBException + */ + public Date bind(String notifId, Date date) throws LBException { + try { + stub = lbCredent.getStub(getServer(notifId)); + String host = InetAddress.getLocalHost().getHostName() + ":" + port; + CalendarHolder holder = new CalendarHolder(Calendar.getInstance()); + if (date != null) { + holder.value.setTime(date); + } else { + holder.value.setTime(new Date(System.currentTimeMillis() + 86400000)); + } + stub.notifBind(notifId, host, holder); + return holder.value.getTime(); + } catch (RemoteException ex) { + throw new LBException(ex); + } catch (UnknownHostException ex) { + throw new LBException(ex); + } + } + + /** + * this method is used to tell the client to start listening for incomming + * connections on the local port, with the specified timeout + * + * @param timeout read timeout + * @return comprehensible information, pulled from the received message + * @throws LBException + */ + public JobStatus receive(int timeout) throws LBException { + SSL ssl = new SSL(); + ssl.setProxy(keyStore); + ILProtoReceiver receiver = null; + String received = null; + try { + if(socket == null) { + socket = ssl.accept(port, timeout); + } + receiver = new ILProtoReceiver(socket); + if((received = receiver.receiveMessage()) == null) { + socket = ssl.accept(port, timeout); + receiver = new ILProtoReceiver(socket); + received = receiver.receiveMessage(); + } + receiver.sendReply(0, 0, "success"); + NotifParser parser = new NotifParser(received); + notifId = parser.getNotifId(); + return parser.getJobInfo(); + } catch (IOException ex) { + throw new LBException(ex); + } catch (KeyManagementException ex) { + throw new LBException(ex); + } catch (KeyStoreException ex) { + throw new LBException(ex); + } catch (NoSuchAlgorithmException ex) { + throw new LBException(ex); + } catch (ParserConfigurationException ex) { + throw new LBException(ex); + } catch (SAXException ex) { + throw new LBException(ex); + } + } +} diff --git a/org.glite.lb.client-java/src/org/glite/lb/NotificationExample.java b/org.glite.lb.client-java/src/org/glite/lb/NotificationExample.java new file mode 100644 index 0000000..6109684 --- /dev/null +++ b/org.glite.lb.client-java/src/org/glite/lb/NotificationExample.java @@ -0,0 +1,95 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package org.glite.lb; + +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.glite.wsdl.types.lb.JobStatus; +import org.glite.wsdl.types.lb.QueryAttr; +import org.glite.wsdl.types.lb.QueryConditions; +import org.glite.wsdl.types.lb.QueryOp; +import org.glite.wsdl.types.lb.QueryRecValue; +import org.glite.wsdl.types.lb.QueryRecord; +import org.glite.wsdl.types.lb.StatName; + +/** + * This is an example on how to use the package org.glite.lb.notif_java + * + * + * @author Kopac + */ +public class NotificationExample { + + /** + * example method on how to use this package + * + * @param args parameters for the methods, order as follows: + *

+ * 1. path to user's certificate + *

+ * 2. path to a list of trusted certificates + *

+ * 3. owner ID + *

+ * 4. port number + *

+ * 5. LB server address + */ + public static void main(String[] args){ + try { + + //creation of an instance of class that prepares SSL connection for + //webservice, first of the two possibilities is used + LBCredentials credentials = new LBCredentials(args[0], args[1]); + + //creation of QueryConditions instance. this one tells the notification to + //be associated with jobs of an owner and that don't have a tag. meh + QueryRecValue value1 = new QueryRecValue(null, args[2], null); + QueryRecord[] record = new QueryRecord[]{new QueryRecord(QueryOp.EQUAL, value1, null)}; + QueryConditions[] conditions = new QueryConditions[]{new + QueryConditions(QueryAttr.OWNER, null, StatName.SUBMITTED, record)}; + + //creating an instance of NotificationImpl, the argument is port number + Notification notif = new Notification(Integer.parseInt(args[3]), credentials); + + Date date = new Date(System.currentTimeMillis()+86400000); + + //registering a new notification, first parameter is LB server address in String, + //second previously created QueryConditions, there are no JobFlags provided + //and the last one tells the server to keep it active for two days + notif.newNotif(args[4], conditions, null, date); + + //id of the newly created notification + String notifId = notif.getNotifid(); + + //refreshing the previously created notification by two days + notif.refresh(notifId, date); + + //tells the client to read incomming notifications with a timeout of 1 minute + JobStatus state = notif.receive(60000); + //from here on, state can be used to pick the desired info using its + //get methods + + //like this + System.out.println(state.getAcl()); + System.out.println(state.getCancelReason()); + System.out.println(state.getJobId()); + + //creates a new instance of NotificationImpl with the port 14342 and + //binds the notification to this new instance, meaning to a new local + //address + Notification notif2 = new Notification(14342, credentials); + notif2.bind(notifId, date); + + //lastly, the notification is dropped + notif2.drop(notifId); + + } catch (LBException ex) { + Logger.getLogger(NotificationExample.class.getName()).log(Level.SEVERE, null, ex); + } + } +} diff --git a/org.glite.lb.client-java/src/org/glite/lb/ServerConnection.java b/org.glite.lb.client-java/src/org/glite/lb/ServerConnection.java new file mode 100644 index 0000000..2eaadf8 --- /dev/null +++ b/org.glite.lb.client-java/src/org/glite/lb/ServerConnection.java @@ -0,0 +1,521 @@ + +package org.glite.lb; + +import holders.StringArrayHolder; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; +import org.apache.axis.client.Stub; +import org.apache.log4j.PropertyConfigurator; +import org.glite.jobid.Jobid; +import org.glite.lb.Event; +import org.glite.lb.Level; +import org.glite.wsdl.services.lb.LoggingAndBookkeepingPortType; +import org.glite.wsdl.types.lb.JobFlagsValue; +import org.glite.wsdl.types.lb.JobStatus; +import org.glite.wsdl.types.lb.QueryConditions; +import org.glite.wsdl.types.lb.holders.JobStatusArrayHolder; + +/** + * This class serves for obtaining data from the bookkeeping + * server. + * The L&B service queries come in two flavors: + *

  1. conjunctive query, as in (cond1) or (cond2)
  2. + *
  3. conjunction of disjunctive queries, as in ( (cond1) + * or (cond2) ) and ( (cond3) or (cond4) )
+ * Methods for both query flavors are provided. + * Query methods actually do communicate with the server and + * they are synchronous; their completion can take some time + * not exceeding the query timeout. + * + * @author Tomas Kramec, 207545@mail.muni.cz + */ +public class ServerConnection { + + private String queryServerAddress; + private int queryJobsLimit=0; + private int queryEventsLimit=0; + private QueryResultsFlag queryResultsFlag = QueryResultsFlag.NONE; + private Level eventLogLevel = Level.LEVEL_SYSTEM; + private LoggingAndBookkeepingPortType queryServer; + private LBCredentials lbCredent; + + + /** + * This enum represents flag to indicate handling of too large results. + * In case the result limit is reached: + *
    + *
  1. NONE - means no results are returned at all.
  2. + *
  3. LIMITED - means a result contains at most limit items.
  4. + *
  5. ALL - means all results are returned withouth any limitation.
  6. + *
+ */ + public enum QueryResultsFlag { + /** + * No results are returned at all. + */ + NONE, + /** + * Result contains at most limit items. Limits for job and event + * results can be set by calling appropriate method on the ServerConnection + * instance. + */ + LIMITED, + /** + * Results are returned withouth any limitation. + */ + ALL + } + + /** + * Constructor initializes the context and + * directs new instance to query the given bookkeping server. + * + * @param server String containing the server address (e.g. https://harad.ics.muni.cz:9453). + * @param lbCredent instance of class that holds credentials for SSL authentication + */ + public ServerConnection(String server, LBCredentials lbCredent) throws LBException { + if (server == null) throw new IllegalArgumentException("Server cannot be null"); + if (lbCredent == null) throw new IllegalArgumentException("Credentials cannot be null"); + + PropertyConfigurator.configure("log4j.properties"); + this.lbCredent = lbCredent; + setQueryServerAddress(server); + setQueryTimeout(120); + } + + /** + * Set bookkeeping server. + * Directs the instance to query the given bookkeping server. + * + * @param server String containing the server address (e.g. https://harad.ics.muni.cz:9553). + */ + public void setQueryServerAddress(String server) throws LBException { + if (server == null) throw new IllegalArgumentException("Server cannot be null"); + + queryServer = lbCredent.getStub(server); + queryServerAddress = server; + + } + + /** + * Get address of the bookkeeping server. + * Returns address of the bookkeeping server this instance is + * bound to. + * + * @return Address (https://hostname:port). + */ + public String getQueryServerAddress() { + return queryServerAddress; + } + + /** + * Set query timeout. + * Sets the time interval to wait for server response. + * Default value is 120 seconds. + * + * @param time Time in seconds before the query expires. 0 means no timeout. + */ + public void setQueryTimeout(int time) { + if (time < 0 || time > 1800) + throw new IllegalArgumentException("timeout must be between 0 and 1800"); + + ((Stub) queryServer).setTimeout(time*1000); + + } + + /** + * Get query timeout. + * Returns the time interval this instance waits for server + * response. + * + * @return Number of seconds to wait. 0 means no timeout. + */ + public int getQueryTimeout() { + return ((Stub) queryServer).getTimeout()/1000; + } + + /** + * Set logging level. + * Sets the level for logging the events. + * Possible values:
    + *
  1. EMERGENCY
  2. + *
  3. ALERT
  4. + *
  5. ERROR
  6. + *
  7. WARNING
  8. + *
  9. AUTH
  10. + *
  11. SECURITY
  12. + *
  13. USAGE
  14. + *
  15. SYSTEM
  16. + *
  17. IMPORTANT
  18. + *
  19. DEBUG
  20. + *
+ * Default value is SYSTEM. + * @param loggingLevel level for logging the events + */ + public void setEventLoggingLevel(Level loggingLevel) { + if (loggingLevel == null) throw new IllegalArgumentException("loggingLevel"); + + this.eventLogLevel = loggingLevel; + } + + /** + * Get logging level. + * Returns the level for logging the events. + * + * @return level value + */ + public Level getEventLoggingLevel() { + return eventLogLevel; + } + + /** + * Retrieve the set of single indexed attributes. + * Returns the set of attributes that are indexed on the + * server. Every query must contain at least one indexed + * attribute for performance reason; exception to this rule + * requires setting appropriate paramater on the server and is + * not advised. + *
+ * In the list returned, each element represents a query condition. + * Query attribute of the condition corresponds to the indexed attribute. + * If the query attribute is USERTAG, tagName is its name; + * if it is TIME, statName is state name. + * + * @return list of QueryConditions + * @throws LBException If some communication or server problem occurs. + */ + public List getIndexedAttrs() throws LBException { + try { + QueryConditions[] cond = queryServer.getIndexedAttrs(""); + List indexedAttrs = new ArrayList(); + for (int i = 0; i < cond.length; i++) { + indexedAttrs.add(cond[i]); + } + return indexedAttrs; + } catch (RemoteException ex) { + throw new LBException(ex); + } + } + + /** + * Retrieve hard result set size limit. This + * property is set at the server side. + * + * Returns the hard limit on the number of + * results returned by the bookkeeping server. + * + * @return Server limit. + * @throws LBException If some communication or server problem occurs. + */ + public int getServerLimit() throws LBException { + try { + return queryServer.getServerLimit(""); + } catch (RemoteException ex) { + throw new LBException(ex); + } + } + + /** + * Set the soft result set size limit. + * Sets the maximum number of results this instance is willing + * to obtain when querying for jobs. + * Default value is 0. It means no limits for results. + * + * @param jobsLimit Maximum number of results. 0 for no limit. + */ + public void setQueryJobsLimit(int jobsLimit) { + if (jobsLimit < 0) throw new IllegalArgumentException("jobsLimit"); + this.queryJobsLimit = jobsLimit; + } + + /** + * Get soft result set size limit. + * Gets the maximum number of results this instance is willing + * to obtain when querying for jobs. + * + * @return queryJobsLimit Maximum number of results. + */ + public int getQueryJobsLimit() { + return queryJobsLimit; + } + + /** + * Set the soft result set size limit. + * Sets the maximum number of results this instance is willing + * to obtain when querying for events. + * Default value is 0. It means no limits for results. + * + * @param eventsLimit Maximum number of results. 0 for no limit. + */ + public void setQueryEventsLimit(int eventsLimit) { + if (eventsLimit < 0) throw new IllegalArgumentException("eventsLimit"); + this.queryEventsLimit = eventsLimit; + } + + /** + * Get soft result set size limit. + * Gets the maximum number of results this instance is willing + * to obtain when querying for events. + * + * @return queryEventsLimit Soft limit. + */ + public int getQueryEventsLimit() { + return queryEventsLimit; + } + + /** + * Sets the flag indicating the way of handling of too large query results. + * Default is NONE. + * + * @param flag One of NONE, LIMITED or ALL. + */ + public void setQueryResultsFlag(QueryResultsFlag flag) { + if (flag == null) throw new IllegalArgumentException("flag"); + + queryResultsFlag = flag; + } + + /** + * Gest the flag indicating the way of handling of too large query results. + * + * @return queryResultsFlag + */ + public QueryResultsFlag getQueryResultsFlag() { + return queryResultsFlag; + } + + /** + * Retrieve all events satisfying the conjunctive-disjunctive + * query records. + * Returns all events belonging to the jobs specified by + * jobCond and satisfying queryCond. The + * conditions are given in conjunctive-disjunctive form + * ((cond1 OR cond2 OR ...) AND ...) + * + * @param jobCond List of conditions on jobs. + * @param eventCond List of coditions on events. + * @return eventList List of Event objects representing L&B events. + * @throws LBException If some communication or server problem occurs. + */ + public List queryEvents(List jobCond, + List eventCond) throws LBException { + + if (jobCond == null) throw new IllegalArgumentException("jobCond cannot be null"); + if (eventCond == null) throw new IllegalArgumentException("eventCond cannot be null"); + + org.glite.wsdl.types.lb.Event[] events = null; + try { + events = queryServer.queryEvents(jobCond.toArray(new QueryConditions[jobCond.size()]), eventCond.toArray(new QueryConditions[eventCond.size()])); + } catch (RemoteException ex) { + throw new LBException(ex); + } + + List eventList= new ArrayList(); + + if (events!= null) { + int queryResults; + //if the events limit is reached + if (queryEventsLimit!=0 && events.length > queryEventsLimit) { + queryResults = getResultSetSize(queryEventsLimit, events.length); + } else queryResults = events.length; + + EventConvertor convertor = new EventConvertor(); + for (int i=0;i queryJobs(List query) throws LBException { + if (query == null) throw new IllegalArgumentException("query cannot be null"); + + StringArrayHolder jobHolder = new StringArrayHolder(); + try { + queryServer.queryJobs(query.toArray(new QueryConditions[query.size()]), new JobFlagsValue[]{}, jobHolder, new JobStatusArrayHolder()); + } catch (RemoteException ex) { + throw new LBException(ex); + } + + List jobList = new ArrayList(); + + if (jobHolder.value!= null) { + int queryResults; + int jobsCount = jobHolder.value.length; + //if the jobs limit is reached + if (queryJobsLimit!=0 && jobsCount > queryJobsLimit) { + queryResults = getResultSetSize(queryJobsLimit, jobsCount); + } else queryResults = jobsCount; + + for (int i=0;i queryJobStates(List query, + JobFlagsValue[] flags) throws LBException { + if (query == null) throw new IllegalArgumentException("query cannot be null"); + + JobStatusArrayHolder jobStatusHolder = new JobStatusArrayHolder(); + try { + queryServer.queryJobs(query.toArray(new QueryConditions[query.size()]), flags, new StringArrayHolder(), jobStatusHolder); + } catch (RemoteException ex) { + throw new LBException(ex); + } + + List jobStates= new ArrayList(); + + if (jobStatusHolder.value!= null) { + int queryResults; + int jobsCount = jobStatusHolder.value.length; + //if the jobs limit is reached + if (queryJobsLimit!=0 && jobsCount > queryJobsLimit) { + queryResults = getResultSetSize(queryJobsLimit, jobsCount); + } else queryResults = jobsCount; + + for (int i=0;i userJobStates() throws LBException { + JobStatusArrayHolder jobStatusHolder = new JobStatusArrayHolder(); + try { + queryServer.userJobs(new StringArrayHolder(), jobStatusHolder); + } catch (RemoteException ex) { + throw new LBException(ex); + } + + List jobStates= new ArrayList(); + + if (jobStatusHolder.value!= null) { + int queryResults; + int jobsCount = jobStatusHolder.value.length; + //if the jobs limit is reached + if (queryJobsLimit!=0 && jobsCount > queryJobsLimit) { + queryResults = getResultSetSize(queryJobsLimit, jobsCount); + } else queryResults = jobsCount; + + for (int i=0;i userJobs() throws LBException { + StringArrayHolder jobHolder = new StringArrayHolder(); + try { + queryServer.userJobs(jobHolder, new JobStatusArrayHolder()); + } catch (RemoteException ex) { + throw new LBException(ex); + } + + List jobs= new ArrayList(); + + if (jobHolder.value!= null) { + int queryResults; + int jobsCount = jobHolder.value.length; + //if the jobs limit is reached + if (queryJobsLimit!=0 && jobsCount > queryJobsLimit) { + queryResults = getResultSetSize(queryJobsLimit, jobsCount); + } else queryResults = jobsCount; + + for (int i=0;i idx = sc.getIndexedAttrs(); + for (int i=0;i jobs = sc.userJobs(); + //get their states + List js = sc.userJobStates(); + Iterator it = jobs.iterator(); + Iterator itJs = js.iterator(); + while (it.hasNext()) { + System.out.println(it.next()); + System.out.println(jobStatusToString(itJs.next())); + } + + //Demonstration of Job class + System.out.println(); + System.out.println("----------------JOB----------------"); + + //create new Job + Job myJob = new Job(args[7], sc); + //print job state info + System.out.println(); + System.out.println("Status: " + jobStatusToString(myJob.getStatus(null))); + + //print info about job's events + System.out.println(); + List events = myJob.getEvents(); + System.out.println("Found "+events.size()+" events:"); + for (int i=0;i recList = new ArrayList(); + int port = Integer.parseInt((args[6].split(":"))[2]); + for (int i=0;i interval) + QueryConditions condOnTime = new QueryConditions(QueryAttr.TIME, null, StatName.SUBMITTED, timeRec); + + //create QueryConditions list representing this formula: + //(JOBID='jobId1' or JOBID='jobId2 or ...) AND (TIME is in interval) + //where jobId1,... are ids of user's jobs + List condList = new ArrayList(); + condList.add(condOnJobid); + condList.add(condOnTime); + + //get all jobs matching the given conditions + List jobResult = sc.queryJobs(condList); + //get all their states + List jobStatesResult = sc.queryJobStates(condList, null); + + //Print information about results + Calendar calendar = new GregorianCalendar(); + DateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); + System.out.println(); + System.out.print("Jobs registered "); + calendar.setTimeInMillis(timeFrom.getT().getTvSec()*1000); + System.out.print("from "+ df.format(calendar.getTime())+" "); + calendar.setTimeInMillis(timeTo.getT().getTvSec()*1000); + System.out.print("to "+ df.format(calendar.getTime())+"\n"); + Iterator jobsit = jobResult.iterator(); + Iterator statusit = jobStatesResult.iterator(); + while (jobsit.hasNext()) { + System.out.println(jobsit.next()); + System.out.println(jobStatusToString(statusit.next())); + } + } + + } + } +} -- 1.8.2.3