From c86f2d04454b03720c169b1b3c12ad57878096b7 Mon Sep 17 00:00:00 2001 From: "Carlos Martin and Ruben S. Montero" Date: Sat, 20 Mar 2010 00:49:20 +0100 Subject: [PATCH] feature #198: Base Classes for the OCA API - JAVA Bindings --- src/oca/java/build.sh | 105 ++++++++ src/oca/java/share/examples/SessionInit.class | Bin 0 -> 1006 bytes .../src/org/opennebula/client/Client.java | 227 ++++++++++++++++++ .../org/opennebula/client/OneResponse.java | 80 ++++++ .../java/src/org/opennebula/client/Pool.java | 125 ++++++++++ .../org/opennebula/client/PoolElement.java | 156 ++++++++++++ 6 files changed, 693 insertions(+) create mode 100755 src/oca/java/build.sh create mode 100644 src/oca/java/share/examples/SessionInit.class create mode 100644 src/oca/java/src/org/opennebula/client/Client.java create mode 100644 src/oca/java/src/org/opennebula/client/OneResponse.java create mode 100644 src/oca/java/src/org/opennebula/client/Pool.java create mode 100644 src/oca/java/src/org/opennebula/client/PoolElement.java diff --git a/src/oca/java/build.sh b/src/oca/java/build.sh new file mode 100755 index 0000000000..62938917da --- /dev/null +++ b/src/oca/java/build.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# -------------------------------------------------------------------------- # +# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); you may # +# not use this file except in compliance with the License. You may obtain # +# a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +#--------------------------------------------------------------------------- # + +#------------------------------------------------------------------------------- +# DIR DEFINITIONS +#------------------------------------------------------------------------------- + +DOC_DIR="./doc" +BIN_DIR="./bin" +JAR_DIR="./jar/org.opennebula.client.jar" +LIB_DIR="./lib" +#------------------------------------------------------------------------------- +# COMMAND LINE PARSING +#------------------------------------------------------------------------------- +usage() { + echo + echo "Usage: build.sh [-d ] [-h] [-s]" + echo + echo "-d: build the documentation" + echo "-s: compile the examples" + echo "-h: prints this help" +} +#------------------------------------------------------------------------------- + +TEMP_OPT=`getopt -o hsd -n 'build.sh' -- "$@"` + +eval set -- "$TEMP_OPT" + +DO_DOC="no" +DO_EXA="no" + +while true ; do + case "$1" in + -h) usage; exit 0;; + -d) DO_DOC="yes"; shift ;; + -s) DO_EXA="yes"; shift ;; + --) shift ; break ;; + *) usage; exit 1 ;; + esac +done + +#------------------------------------------------------------------------------- +# BUILD FUNCTIONS +#------------------------------------------------------------------------------- + +do_documentation() +{ + echo "Generating javadocs..." + + rm -rf $DOC_DIR > /dev/null 2>&1 + mkdir -p $DOC_DIR + javadoc -quiet -classpath $LIB_DIR"/*" -d $DOC_DIR \ + -sourcepath ./src/ \ + -subpackages org.opennebula \ + -windowtitle 'OpenNebula Cloud API' \ + -doctitle 'OpenNebula Cloud API Specification' \ + -header 'OpenNebula
Java API' \ + -bottom 'Visit OpenNebula.org
Copyright 2002-2010 © +OpenNebula Project Leads (OpenNebula.org).' +} + +do_jar() +{ + rm -rf $BIN_DIR > /dev/null 2>&1 + mkdir -p $BIN_DIR + + echo "Compiling java files into class files..." + javac -d $BIN_DIR -cp $LIB_DIR"/*" `find src -name *.java` + + if [ $? -eq 0 ]; then + echo "Packaging class files in a jar..." + jar cf $JAR_DIR -C $BIN_DIR org + fi +} + +do_examples() +{ + echo "Compiling OpenNebula Cloud API Examples..." +} + +do_jar + +if [ "$DO_DOC" = "yes" ] ; then + do_documentation +fi + +if [ "$DO_EXA" = "yes" ] ; then + do_examples +fi diff --git a/src/oca/java/share/examples/SessionInit.class b/src/oca/java/share/examples/SessionInit.class new file mode 100644 index 0000000000000000000000000000000000000000..77ffc19899176b5da1377a35df98e8916c51e5c4 GIT binary patch literal 1006 zcmZuwYflqF6g^Yg?si#T1&U&!iz1X)tN7$08i^(*1x#u*XbhRQlV+jaZMIuc|B#>Y zLz_rq{Opf1o@p^iU^lsU_TF>ubN2V2A3p)C;+cgpWD;;NW8$WTTbMO)+d>Q(19KJ( z%xh!T#Da-M6HD5)Y+%L0ATlO$TIaR6W8kiVRe|`X>$%~UKwoy@tw40!uPA|`g6pZ> zR=uQxH?mZtC0duRCy>g1C>+X9GGCKkHD3$^*Q;)5;y^K!<)aL>~^4J$Y_pqJaz2sHI%ojO0Al7&X~De&cH(( zkMLL^?FZGoPmI^afD9jqGP-_Y1hxc=9oifQL?`Auepn6fmlO>$jV;5 zAgijv9kwc~+{RdT;WA3V=*FmV&+}JAxy;aukIM$vP!U+Uf^s>_*_7CZ8T!4!VFshT z*ZPTo4Io81!LKQ5K^F4kz`*R(No0sJBSy) zVX#2kQX6I)3GKDcbU(F5C2i5c0D+DMo??)^q2zEIBR$ec8%aV>7~jM&OEr+=RobME zbn97#&CDr!C+W`+k!Cy7d}lDn-!VtIh9xqxycdhu$1-k^VIx{JkhVTjq}j(5Z4pfW E1@d0$i2wiq literal 0 HcmV?d00001 diff --git a/src/oca/java/src/org/opennebula/client/Client.java b/src/oca/java/src/org/opennebula/client/Client.java new file mode 100644 index 0000000000..1fea68b32d --- /dev/null +++ b/src/oca/java/src/org/opennebula/client/Client.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.opennebula.client; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.xmlrpc.XmlRpcException; +import org.apache.xmlrpc.client.XmlRpcClient; +import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; + + +/** + * This class represents the connection with the core and handles the + * xml-rpc calls. + * + */ +public class Client { + + //-------------------------------------------------------------------------- + // PUBLIC INTERFACE + //-------------------------------------------------------------------------- + + /** + * Creates a new xml-rpc client with default options: + * the auth. file will be assumed to be at $ONE_AUTH, and + * the endpoint will be set to $ONE_XMLRPC. + *
+ * It is the equivalent of Client(null, null). + * + * @throws Exception if one authorization file cannot be located. + */ + public Client() throws Exception + { + setOneAuth(null); + setOneEndPoint(null); + } + + /** + * Creates a new xml-rpc client with specified options. + * + * @param secret A string containing the ONE user:password tuple. + * Can be null + * @param endpoint Where the rpc server is listening, must be something + * like "http://localhost:2633/RPC2". Can be null + * @throws Exception if the authorization options are invalid + */ + public Client(String secret, String endpoint) throws Exception + { + setOneAuth(secret); + setOneEndPoint(endpoint); + } + + /** + * Performs an XML-RPC call. + * + * @param action ONE action + * @param args ONE arguments + * @return The server's xml-rpc response encapsulated + */ + public OneResponse call(String action, Object...args) + { + boolean success = false; + String msg = null; + + try + { + Object[] params = new Object[args.length + 1]; + + params[0] = oneAuth; + + for(int i=0; i 1) + { + try + { + msg = (String) result[1]; + } + catch (ClassCastException e) + { + // The result may be an Integer + msg = ((Integer) result[1]).toString(); + } + } + + + } + catch (XmlRpcException e) + { + msg = e.getMessage(); + } + + return new OneResponse(success, msg); + } + + //-------------------------------------------------------------------------- + // PRIVATE ATTRIBUTES AND METHODS + //-------------------------------------------------------------------------- + + private String oneAuth; + private String oneEndPoint; + + private XmlRpcClient client; + + private void setOneAuth(String secret) throws Exception + { + String oneSecret = secret; + + try + { + if(oneSecret == null) + { + String oneAuthEnv = System.getenv("ONE_AUTH"); + File authFile; + + if ( oneAuthEnv != null && oneAuthEnv.length() != 0) + { + authFile = new File(oneAuthEnv); + } + else + { + authFile = new File(System.getenv("HOME")+"/.one/one_auth"); + } + + oneSecret = + (new BufferedReader(new FileReader(authFile))).readLine(); + } + + String[] token = oneSecret.split(":"); + + if(token.length != 2 ) + { + throw new Exception("Wrong format for authorization string: " + + oneSecret); + } + + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] digest = md.digest(token[1].getBytes()); + + String hash = ""; + + for(byte aux : digest) + { + int b = aux & 0xff; + + if (Integer.toHexString(b).length() == 1) + { + hash += "0"; + } + + hash += Integer.toHexString(b); + } + + oneAuth = token[0] + ":" + hash; + } + catch (FileNotFoundException e) + { + throw new Exception("ONE_AUTH file not present"); + } + catch (NoSuchAlgorithmException e) + { + throw new Exception("Error initializing MessageDigest with SHA-1"); + } + } + + private void setOneEndPoint(String endpoint) throws Exception + { + oneEndPoint = "http://localhost:2633/RPC2"; + + if(endpoint != null) + { + oneEndPoint = endpoint; + } + else + { + String oneXmlRpcEnv = System.getenv("ONE_XMLRPC"); + + if ( oneXmlRpcEnv != null && oneXmlRpcEnv.length() != 0 ) + { + oneEndPoint = oneXmlRpcEnv; + } + } + + XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl(); + + try + { + config.setServerURL(new URL(oneEndPoint)); + } + catch (MalformedURLException e) + { + throw new Exception("The URL "+oneEndPoint+" is malformed."); + } + + client = new XmlRpcClient(); + client.setConfig(config); + } +} diff --git a/src/oca/java/src/org/opennebula/client/OneResponse.java b/src/oca/java/src/org/opennebula/client/OneResponse.java new file mode 100644 index 0000000000..98201ec769 --- /dev/null +++ b/src/oca/java/src/org/opennebula/client/OneResponse.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.opennebula.client; + +/** + * This class encapsulates OpenNebula's XML-RPC responses. Each response + * carries a boolean indicating if it is an error. It can also contain a + * success message, or an error message. + */ +public class OneResponse { + /** + * Creates a new response. + * + * @param success Indicates if the call was successful, and if + * the message is an error or an information string. + * @param message String containing the response message, or + * the error message. + */ + public OneResponse(boolean success, String message) + { + this.success = success; + this.msg = message; + } + + /** + * Returns true if the call resulted in error. + * + * @return True if the call resulted in error. + */ + public boolean isError() + { + return !success; + } + + /** + * Returns a string containing the error message, or null + * if the response isn't an error. + * + * @return A string containing the error message, or null + * if the response isn't an error. + */ + public String getErrorMessage() + { + return success ? null : msg; + } + + /** + * Returns a string containing the response information, or + * null if the response was an error. Note that the success + * message could be also null. + * + * @return A string containing the response information, or + * null if the response was an error. Note that the success + * message could be also null. + */ + public String getMessage() + { + return success ? msg : null; + } + + // ------------------------------------------------------------------------ + // PRIVATE ATTRIBUTES + // ------------------------------------------------------------------------ + + private boolean success; + private String msg; +} diff --git a/src/oca/java/src/org/opennebula/client/Pool.java b/src/oca/java/src/org/opennebula/client/Pool.java new file mode 100644 index 0000000000..8762ce5088 --- /dev/null +++ b/src/oca/java/src/org/opennebula/client/Pool.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.opennebula.client; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +/** + * Represents a generic OpenNebula Pool in XML format + * and provides the basic functionality to handle the Pool elements. + */ +public abstract class Pool{ + + protected Client client; + + protected String elementName; + protected NodeList poolElements; + + /** + * Sets the Pool attributes. + * @param elementName Name of the PoolElement's xml element + * @param client XML-RPC client which will handle calls + */ + protected Pool(String elementName, Client client) + { + this.elementName = elementName; + this.client = client; + } + + /** + * The factory method returns a suitable PoolElement object from + * an XML node. Each Pool must implement the corresponding factory method. + * + * @param node XML Dom node to build the PoolElement from + * @return The corresponding PoolElement + */ + public abstract PoolElement factory(Node node); + + /** + * After a *pool.info call, this method builds the internal xml + * representation of the pool. + * @param info The XML-RPC *pool.info response + */ + public void processInfo(OneResponse info) + { + if (info.isError()) + { + return; + } + + try + { + DocumentBuilder builder; + Document doc; + Element xml; + + builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + doc = builder.parse( + new ByteArrayInputStream(info.getMessage().getBytes())); + xml = doc.getDocumentElement(); + + poolElements = xml.getElementsByTagName(elementName); + } + catch (ParserConfigurationException e) {} + catch (SAXException e) {} + catch (IOException e) {} + } + + /** + * Returns the indexth element in the pool. If index is greater than or + * equal to the number of elements in the pool, this returns null. + * + * @param index Index of the element. + * @return The element at the indexth position in the pool, or + * null if that is not a valid index. + */ + public PoolElement item(int index) + { + PoolElement the_element = null; + + if (poolElements != null) + { + Node node =poolElements.item(index); + + if (node != null) + { + the_element = factory(node); + } + } + + return the_element; + } + + /** + * The number of elements in the pool. + * @return The number of elements in the pool. + */ + public int getLength() + { + return poolElements == null ? 0 : poolElements.getLength(); + } +} \ No newline at end of file diff --git a/src/oca/java/src/org/opennebula/client/PoolElement.java b/src/oca/java/src/org/opennebula/client/PoolElement.java new file mode 100644 index 0000000000..cc93e948fd --- /dev/null +++ b/src/oca/java/src/org/opennebula/client/PoolElement.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.opennebula.client; + +import java.io.ByteArrayInputStream; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * Represents a generic element of a Pool in + * XML format. + * + */ +public abstract class PoolElement { + + protected static XPath xpath; + + protected int id; + protected Node xml; + + protected Client client; + + /** + * Creates a new PoolElement with the specified attributes. + * @param id Id of the element. + * @param client XML-RPC Client. + * @param root Name of the xml's root element. + */ + protected PoolElement(int id, Client client) + { + if(xpath == null) + { + XPathFactory factory = XPathFactory.newInstance(); + xpath = factory.newXPath(); + } + + this.id = id; + this.client = client; + } + + /** + * Creates a new PoolElement from the xml provided. + * + * @param client XML-RPC Client. + * @param xmlElement XML representation of the element. + * @param root Name of the xml's root element. + */ + protected PoolElement(Node xmlElement, Client client) + { + if(xpath == null) + { + XPathFactory factory = XPathFactory.newInstance(); + xpath = factory.newXPath(); + } + + this.xml = xmlElement; + this.client = client; + this.id = Integer.parseInt(xpath("id")); + } + + /** + * After a *.info call, this method builds the internal xml + * representation of the pool. + * @param info The XML-RPC *.info response + */ + protected void processInfo(OneResponse info) + { + if (info.isError()) + { + return; + } + + try + { + DocumentBuilder builder = + DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.parse( + new ByteArrayInputStream(info.getMessage().getBytes())); + + xml = doc.getDocumentElement(); + } + catch (Exception e) {} + } + + /** + * Returns the element's ID. + * @return the element's ID. + */ + public String getId() + { + return Integer.toString(id); + } + + /** + * Returns the element's name. + * @return the element's name. + */ + public String getName() + { + return xpath("name"); + } + + /** + * Performs an xpath evaluation for the "state" expression. + * @return The value of the STATE element. + */ + public int state() + { + String state = xpath("state"); + + return state != null ? Integer.parseInt( state ) : -1; + } + + /** + * Evaluates an XPath expression and returns the result as a String. + * If the internal xml representation is not built, returns null. The + * subclass method info() must be called before. + * + * @param expression The XPath expression. + * @return The String that is the result of evaluating the + * expression and converting the result to a String. Null if + * the internal xml representation is not built. + */ + public String xpath(String expression) + { + String result = null; + + try + { + result = xpath.evaluate(expression.toUpperCase(), xml); + } + catch (XPathExpressionException e) {} + + return result; + } +}