/*
*============================================================================
* This library is free software; you can redistribute it and/or
* modify it under the terms of version 2.1 of the GNU Lesser General Public
* License as published by the Free Software Foundation.
*
* This library 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*============================================================================
* Copyright (C) 2007 XenSource Inc.
*============================================================================
*/
package com.xensource.xenapi;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
public class Connection {
/**
* true if the connection is to the Rio edition of XenServer. Certain function calls are not allowed.
*/
public Boolean rioConnection=false;
private String sessionReference; // the opaque reference to the session used by this connection
private final XmlRpcClient client; // as seen by the xmlrpc library. From our point of view it's a server
/**
* Create a connection to a particular server using a given username and password. This object
* can then be passed in to any other API calls.
*
* To login to a miami box we call login_with_password(username, password, "1.2")
* on rio this call fails and we should use login_with_password(username,password) instead, and note that we are talking to a rio host
* so that we can refuse to make certain miami-specific calls
*/
public Connection (String client, String username, String password)
throws java.net.MalformedURLException, org.apache.xmlrpc.XmlRpcException, Types.BadServerResponse, Types.SessionAuthenticationFailed
{
final String ApiVersion = "1.2";
this.client = getClientFromURL(client);
try{
//first try to login the modern way
this.sessionReference = loginWithPassword(this.client, username, password, ApiVersion);
} catch (Types.BadServerResponse e) {
//oops, something went wrong
Object[] errDesc = (Object[]) e.response.get("ErrorDescription");
//was the problem that the host was running rio? If so it will have complained that it got three parameters
//instead of two. Let us carefully verify the details of this complaint
if ( (0 == ((String) errDesc[0]).compareTo("MESSAGE_PARAMETER_COUNT_MISMATCH"))
&& (0 == ((String) errDesc[1]).compareTo("session.login_with_password"))
&& (0 == ((String) errDesc[2]).compareTo("2"))
&& (0 == ((String) errDesc[3]).compareTo("3")) ) {
//and if so, we can have another go, using the older login method, and see how that goes.
this.sessionReference = loginWithPassword(this.client, username, password);
//success!. Note that we are talking to an old host on this connection
this.rioConnection=true;
} else {
//Hmm... Can't solve this here. Let upstairs know about the problem.
throw e;
}
}
}
protected void finalize()
throws Throwable
{
dispose();
super.finalize();
}
public void dispose()
throws Types.BadServerResponse,
XmlRpcException
{
if (sessionReference != null)
{
String method_call = "session.logout";
Object[] method_params = {Marshalling.toXMLRPC(this.sessionReference)};
Map response = (Map) client.execute(method_call, method_params);
sessionReference = null;
if(response.get("Status").equals("Success")) {
return;
}
throw new Types.BadServerResponse(response);
}
}
private static String loginWithPassword(XmlRpcClient client, String username, String password) throws
Types.BadServerResponse,
XmlRpcException,
Types.SessionAuthenticationFailed
{
String method_call = "session.login_with_password";
Object[] method_params = {Marshalling.toXMLRPC(username), Marshalling.toXMLRPC(password)};
Map response = (Map) client.execute(method_call, method_params);
if(response.get("Status").equals("Success")) {
return (String) response.get("Value");
} else if(response.get("Status").equals("Failure")) {
Object[] error = (Object[]) response.get("ErrorDescription");
if(error[0].equals("SESSION_AUTHENTICATION_FAILED")) {
throw new Types.SessionAuthenticationFailed();
}
}
throw new Types.BadServerResponse(response);
}
private static String loginWithPassword(XmlRpcClient client, String username, String password, String ApiVersion) throws
Types.BadServerResponse,
XmlRpcException,
Types.SessionAuthenticationFailed
{
String method_call = "session.login_with_password";
Object[] method_params = {Marshalling.toXMLRPC(username), Marshalling.toXMLRPC(password), Marshalling.toXMLRPC(ApiVersion)};
Map response = (Map) client.execute(method_call, method_params);
if(response.get("Status").equals("Success")) {
return (String) response.get("Value");
} else if(response.get("Status").equals("Failure")) {
Object[] error = (Object[]) response.get("ErrorDescription");
if(error[0].equals("SESSION_AUTHENTICATION_FAILED")) {
throw new Types.SessionAuthenticationFailed();
}
}
throw new Types.BadServerResponse(response);
}
/*
* Annoying boilerplate.
*/
private XmlRpcClient getClientFromURL(String s) throws java.net.MalformedURLException
{
URL url = new URL(s);
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(url);
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
return client;
}
/*
* Because the binding calls are constructing their own parameter lists, they need to be able to get to
* the session reference directly. This is all rather ugly and needs redone
*/
String getSessionReference(){
return this.sessionReference;
}
/*
* Similarly, we allow (auto-generated parts of) the bindings direct access to the XML rpc calling mechanism.
*/
Map dispatch(String method_call, Object[] method_params) throws XmlRpcException {
return (Map) client.execute(method_call, method_params);
}
}