Java tutorial
/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program 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 Lesser General Public License for more details. * * Copyright 2009 Pentaho Corporation. All rights reserved. * * Created Nov 4, 2009 * @author jdixon */ package org.pentaho.platform.dataaccess.client; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.impl.builder.StAXOMBuilder; import org.apache.axis2.AxisFault; import org.apache.axis2.databinding.utils.BeanUtil; import org.apache.axis2.engine.ObjectSupplier; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.Node; import org.pentaho.database.model.DatabaseAccessType; import org.pentaho.database.model.DatabaseConnection; import org.pentaho.database.model.IDatabaseConnection; import org.pentaho.platform.dataaccess.datasource.IConnection; import org.pentaho.platform.dataaccess.datasource.beans.Connection; import org.pentaho.platform.dataaccess.datasource.wizard.service.ConnectionServiceException; import org.pentaho.platform.dataaccess.datasource.wizard.service.gwt.IConnectionService; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; /** * Provides a simple API for working with the connections web service. * * This class implements IConnectionService * {@link org.pentaho.platform.dataaccess.datasource.wizard.service.gwt.IConnectionService } * * Example code for using this class: * * ConnectionServiceClient serviceClient = new ConnectionServiceClient(); * serviceClient.setHost("http://localhost:8080/pentaho"); * serviceClient.setUserId("joe"); * serviceClient.setPassword("password"); * List<IConnection> connections = serviceClient.getConnections(); * * @author jamesdixon * */ public class ConnectionServiceClient implements IConnectionService, ObjectSupplier { protected static final Log logger = LogFactory.getLog(ConnectionServiceClient.class); private String serviceUrl; // e.g. http://someserver:port/pentaho private String userId; private String password; private HttpClient httpClientInstance = null; /** * Returns an instance of an HttpClient. Only one is created per * ConnectionServiceClient so all calls should be made synchronously. * @return The HTTP client to be used for web service calls */ private HttpClient getClient() { if (httpClientInstance == null) { httpClientInstance = new HttpClient(); if ((userId != null) && (userId.length() > 0) && (password != null) && (password.length() > 0)) { Credentials creds = new UsernamePasswordCredentials(userId, password); httpClientInstance.getState().setCredentials(AuthScope.ANY, creds); httpClientInstance.getParams().setAuthenticationPreemptive(true); } } return httpClientInstance; } /** * Adds a connection to the server's configuration. Returns true if the attempt was successful. * setHost(), setUserId() and setPassword() must be called before this * method is called. * @param connection The connection to be added * @return True if the addition was successful */ public boolean addConnection(IConnection connection) throws ConnectionServiceException { String xml = getConnectionXml(connection); PostMethod callMethod = new PostMethod(serviceUrl + "/addConnection"); //$NON-NLS-1$ // add the xml to the request entity RequestEntity requestEntity = new StringRequestEntity(xml); callMethod.setRequestEntity(requestEntity); // get the result and parse de-serialize it Node node = getResultNode(callMethod); return node != null && Boolean.parseBoolean(this.getNodeText(node)); } public DatabaseConnection convertFromConnection(IConnection connection) throws ConnectionServiceException { String xml = getConnectionXml(connection); PostMethod callMethod = new PostMethod(serviceUrl + "/convertFromConnection"); //$NON-NLS-1$ // add the xml to the request entity RequestEntity requestEntity = new StringRequestEntity(xml); callMethod.setRequestEntity(requestEntity); // get the result and parse de-serialize it Document doc = getResultDocument(callMethod); try { return getResponseDatabaseConnection(doc.getRootElement()); } catch (XMLStreamException e) { e.printStackTrace(); throw new ConnectionServiceException(e); } catch (AxisFault e) { e.printStackTrace(); throw new ConnectionServiceException(e); } catch (Exception e) { e.printStackTrace(); throw new ConnectionServiceException(e); } } public Document serializeDatabaseConnection(DatabaseConnection databaseConnection) { XStream xstream = new XStream(new DomDriver()); xstream.alias("databaseConnection", DatabaseConnection.class); //$NON-NLS-1$ xstream.alias("accessType", DatabaseAccessType.class); //$NON-NLS-1$ String xml = xstream.toXML(databaseConnection); System.out.println(xml); return null; } protected Connection getResponseConnection(Element rootNode) throws XMLStreamException, AxisFault { return (Connection) getResponseObject(Connection.class, rootNode); } protected DatabaseConnection getResponseDatabaseConnection(Element rootNode) throws XMLStreamException, AxisFault { DatabaseConnection result = (DatabaseConnection) getResponseObject(DatabaseConnection.class, rootNode); String accessTypeValue = result.getAccessTypeValue(); DatabaseAccessType accessType = result.getAccessType(); if (accessType == null || accessType.getValue() != accessTypeValue) { result.setAccessType(DatabaseAccessType.valueOf(accessTypeValue)); } return result; } protected Object getResponseObject(Class clazz, Element rootNode) throws XMLStreamException, AxisFault { Object results[] = getResponseObjects(new Object[] { clazz }, rootNode); if (results == null || results.length == 0) { return null; } return results[0]; } protected Object[] getResponseArray(Class clazz, Element rootNode) throws XMLStreamException, AxisFault { // find out how many responses there are List nodes = rootNode.selectNodes("*/*/*"); //$NON-NLS-1$ int count = nodes.size(); Object types[] = new Object[count]; for (int idx = 0; idx < count; idx++) { types[idx] = clazz; } return getResponseObjects(types, rootNode); } protected Object[] getResponseObjects(Object[] types, Element rootNode) throws XMLStreamException, AxisFault { ByteArrayInputStream in = new ByteArrayInputStream(rootNode.asXML().getBytes()); XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in); StAXOMBuilder builder = new StAXOMBuilder(parser); //get the root element (in this case the envelope) OMElement omElement = builder.getDocumentElement(); OMElement bodyElement = omElement.getFirstElement(); OMElement responseElement = bodyElement.getFirstElement(); Object results[] = BeanUtil.deserialize(responseElement, types, this); return results; } public DatabaseConnection deserializeDatabaseConnection(Element rootNode) throws XMLStreamException, AxisFault { System.out.println(rootNode.asXML()); ByteArrayInputStream in = new ByteArrayInputStream(rootNode.asXML().getBytes()); XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(in); StAXOMBuilder builder = new StAXOMBuilder(parser); //get the root element (in this case the envelope) OMElement omElement = builder.getDocumentElement(); OMElement bodyElement = omElement.getFirstElement(); OMElement responseElement = bodyElement.getFirstElement(); Object types[] = new Object[] { DatabaseConnection.class }; Object results[] = BeanUtil.deserialize(responseElement, types, this); if (results == null || results.length == 0) { return null; } return (DatabaseConnection) results[0]; } public IConnection convertToConnection(IDatabaseConnection arg0) throws ConnectionServiceException { // TODO Auto-generated method stub return null; } /** * Deletes a connection from the server's configuration. * Returns true if the attempt was successful. * setHost(), setUserId() and setPassword() must be called before this * method is called. * @param connection The connection to be deleted * @return True if the deletion was successful */ public boolean deleteConnection(IConnection connection) throws ConnectionServiceException { return deleteConnection(connection.getName()); } /** * Deletes a connection from the server's configuration. * Returns true if the attempt was successful. * setHost(), setUserId() and setPassword() must be called before this * method is called. * @param connectionName The name of the connection to be deleted * @return True if the deletion was successful */ public boolean deleteConnection(String connectionName) throws ConnectionServiceException { GetMethod callMethod = new GetMethod(serviceUrl + "/deleteConnectionByName"); //$NON-NLS-1$ callMethod.setQueryString("name=" + connectionName); //$NON-NLS-1$ // get the result and parse de-serialize it Node node = getResultNode(callMethod); return node != null && Boolean.parseBoolean(this.getNodeText(node)); } /** * Returns a specified connection. Returns null is the connection is not * defined in the server's configuration. * setHost(), setUserId() and setPassword() must be called before this * method is called. * @param connectionName The name of the connection to be returned * @return The connection requested */ public IConnection getConnectionByName(String connectionName) throws ConnectionServiceException { PostMethod callMethod = new PostMethod(serviceUrl + "/getConnectionByName"); //$NON-NLS-1$ // add the xml to the request entity String xml = getRequestXml(new Parameter("name", connectionName)); //$NON-NLS-1$ RequestEntity requestEntity = new StringRequestEntity(xml); callMethod.setRequestEntity(requestEntity); // get the result and parse de-serialize it Document resultDoc = getResultDocument(callMethod); try { return getResponseConnection(resultDoc.getRootElement()); } catch (XMLStreamException e) { e.printStackTrace(); throw new ConnectionServiceException(e); } catch (AxisFault e) { e.printStackTrace(); throw new ConnectionServiceException(e); } catch (Exception e) { e.printStackTrace(); throw new ConnectionServiceException(e); } } /** * Generates a SOAP request for an Axis service. * @param params * @return */ protected String getRequestXml(Parameter... params) { Document doc = DocumentHelper.createDocument(); Element envelopeNode = DocumentHelper.createElement("soapenv:Envelope"); //$NON-NLS-1$ envelopeNode.addAttribute("xmlns:soapenv", "http://www.w3.org/2003/05/soap-envelope"); //$NON-NLS-1$ //$NON-NLS-2$ envelopeNode.addAttribute("xmlns:wsa", "http://www.w3.org/2005/08/addressing"); //$NON-NLS-1$ //$NON-NLS-2$ envelopeNode.addAttribute("xmlns:pho", //$NON-NLS-1$ "http://impl.service.wizard.datasource.dataaccess.platform.pentaho.org"); //$NON-NLS-1$ doc.add(envelopeNode); // create a Body node Element bodyNode = DocumentHelper.createElement("soapenv:Body"); //$NON-NLS-1$ envelopeNode.add(bodyNode); if (params == null || params.length == 0) { return doc.asXML(); } // create a parameter called 'parameters' Element parametersNode = DocumentHelper.createElement("pho:parameters"); //$NON-NLS-1$ bodyNode.add(parametersNode); for (Parameter param : params) { // create a parameter called 'name' Element nameNode = DocumentHelper.createElement("pho:" + param.getName()); //$NON-NLS-1$ parametersNode.add(nameNode); nameNode.setText(param.getValue().toString()); nameNode.addAttribute("type", param.getValue().getClass().getCanonicalName()); //$NON-NLS-1$ } return doc.asXML(); } /** * Returns a list of connections known to the server. Each one is an IConnection object. * Returns an empty list if no connections are defined. * setHost(), setUserId() and setPassword() must be called before this * method is called. * @return List of the connections */ public List<IConnection> getConnections() throws ConnectionServiceException { PostMethod callMethod = new PostMethod(serviceUrl + "/getConnections"); //$NON-NLS-1$ String xml = getRequestXml(); RequestEntity requestEntity = new StringRequestEntity(xml); callMethod.setRequestEntity(requestEntity); List<IConnection> connections = new ArrayList<IConnection>(); // get the result and parse de-serialize it Document resultDoc = getResultDocument(callMethod); try { Object connectionArray[] = new Object[0]; connectionArray = getResponseArray(Connection.class, resultDoc.getRootElement()); for (Object connection : connectionArray) { connections.add((Connection) connection); } } catch (XMLStreamException e) { e.printStackTrace(); throw new ConnectionServiceException(e); } catch (AxisFault e) { e.printStackTrace(); throw new ConnectionServiceException(e); } catch (Exception e) { e.printStackTrace(); throw new ConnectionServiceException(e); } return connections; } /** * Returns the text with the provided node. If the node is null, a null String is returned. * @param node Dom4J node * @return Text within the node */ protected String getNodeText(Node node) { if (node == null) { return null; } else { return node.getText(); } } /** * Returns XML for a connection object suitable for submitting to the connection web service * @param connection Connection object to be encoded as XML * @return XML serialization of the connection object */ protected String getConnectionXml(IConnection connection) { Document doc = DocumentHelper.createDocument(); // create a SOAP envelope and specify namespaces Element envelopeNode = DocumentHelper.createElement("soapenv:Envelope"); //$NON-NLS-1$ envelopeNode.addAttribute("xmlns:soapenv", "http://www.w3.org/2003/05/soap-envelope"); //$NON-NLS-1$ //$NON-NLS-2$ envelopeNode.addAttribute("xmlns:wsa", "http://www.w3.org/2005/08/addressing"); //$NON-NLS-1$ //$NON-NLS-2$ envelopeNode.addAttribute("xmlns:pho", //$NON-NLS-1$ "http://impl.service.wizard.datasource.dataaccess.platform.pentaho.org"); //$NON-NLS-1$ doc.add(envelopeNode); // create a Body node Element bodyNode = DocumentHelper.createElement("soapenv:Body"); //$NON-NLS-1$ envelopeNode.add(bodyNode); // create a parameter called 'connection' Element parameterNode = DocumentHelper.createElement("pho:connection"); //$NON-NLS-1$ bodyNode.add(parameterNode); // create a Connection node with a type of org.pentaho.platform.dataaccess.datasource.beans.Connection Element connectionNode = DocumentHelper.createElement("pho:Connection"); //$NON-NLS-1$ connectionNode.addAttribute("type", "org.pentaho.platform.dataaccess.datasource.beans.Connection"); //$NON-NLS-1$ //$NON-NLS-2$ parameterNode.add(connectionNode); // add the driver class of the connection Element node = DocumentHelper.createElement("driverClass"); //$NON-NLS-1$ node.setText(connection.getDriverClass()); connectionNode.add(node); // add the name of the connection node = DocumentHelper.createElement("name"); //$NON-NLS-1$ node.setText(connection.getName()); connectionNode.add(node); // add the password for the connection node = DocumentHelper.createElement("password"); //$NON-NLS-1$ node.setText(connection.getPassword()); connectionNode.add(node); // add the url of the connection node = DocumentHelper.createElement("url"); //$NON-NLS-1$ node.setText(connection.getUrl()); connectionNode.add(node); // add the user name for the connection node = DocumentHelper.createElement("username"); //$NON-NLS-1$ node.setText(connection.getUsername()); connectionNode.add(node); // return the XML return doc.asXML(); } /** * Tests a provided connection on the server. This does not store the * connection on the server, just validates that the connection works * in the server's environment. * @param connection The connection to be tested */ public boolean testConnection(IConnection connection) throws ConnectionServiceException { String xml = getConnectionXml(connection); PostMethod callMethod = new PostMethod(serviceUrl + "/testConnection"); //$NON-NLS-1$ // add the xml to the request entity RequestEntity requestEntity = new StringRequestEntity(xml); callMethod.setRequestEntity(requestEntity); // get the result and parse de-serialize it Node node = getResultNode(callMethod); return node != null && Boolean.parseBoolean(this.getNodeText(node)); } public boolean updateConnection(IConnection connection) throws ConnectionServiceException { String xml = getConnectionXml(connection); PostMethod callMethod = new PostMethod(serviceUrl + "/updateConnection"); //$NON-NLS-1$ // add the xml to the request entity RequestEntity requestEntity = new StringRequestEntity(xml); callMethod.setRequestEntity(requestEntity); // get the result and parse de-serialize it Node node = getResultNode(callMethod); return node != null && Boolean.parseBoolean(this.getNodeText(node)); } /** * Sets the host server, port, and context. For example * http://server:port/pentaho * @param host The host address and context */ public void setHost(String host) { // create the service url serviceUrl = host + "/content/ws-run/soapConnectionService"; //$NON-NLS-1$ } /** * Sets the user id to use to connect to the server * @param userId */ public void setUserId(String userId) { this.userId = userId; } /** * Sets the password to use to connect to the server * @param userId */ public void setPassword(String password) { this.password = password; } protected List getResultNodes(HttpMethod callMethod) throws ConnectionServiceException { Document doc = getResultDocument(callMethod); return doc.selectNodes("//return"); //$NON-NLS-1$ } protected Node getResultNode(HttpMethod callMethod) throws ConnectionServiceException { Document doc = getResultDocument(callMethod); return doc.selectSingleNode("//return"); //$NON-NLS-1$ } /** * Submits an HTTP result with the provided HTTPMethod and returns a dom4j document of the * response * @param callMethod * @return * @throws ConnectionServiceException */ protected Document getResultDocument(HttpMethod callMethod) throws ConnectionServiceException { try { HttpClient client = getClient(); // execute the HTTP call int status = client.executeMethod(callMethod); if (status != HttpStatus.SC_OK) { throw new ConnectionServiceException("Web service call failed with code " + status); //$NON-NLS-1$ } // get the result as a string InputStream in = callMethod.getResponseBodyAsStream(); byte buffer[] = new byte[2048]; int n = in.read(buffer); StringBuilder sb = new StringBuilder(); while (n != -1) { sb.append(new String(buffer, 0, n)); n = in.read(buffer); } String result = sb.toString(); // convert to XML return DocumentHelper.parseText(result); } catch (IOException e) { throw new ConnectionServiceException(e); } catch (DocumentException e) { throw new ConnectionServiceException(e); } } /** * Returns an object for the specified class. * This is in the ObjectSupplier interface used by * the BeanUtil deserialize methods. */ public Object getObject(Class clazz) throws AxisFault { try { System.out.println(clazz.getCanonicalName()); Constructor[] cons = clazz.getConstructors(); if (cons.length > 0) { return clazz.newInstance(); } return null; } catch (IllegalAccessException e) { throw new AxisFault(e.getLocalizedMessage(), e); } catch (InstantiationException e) { throw new AxisFault(e.getLocalizedMessage(), e); } } private class Parameter { public String name; public Object value; public Parameter(String name, Object value) { this.name = name; this.value = value; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } } }