// Copyright 2012 Citrix Systems, Inc. Licensed under the
// Apache License, Version 2.0 (the "License"); you may not use this
// file except in compliance with the License. Citrix Systems, Inc.
// reserves all rights not expressly granted by 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.
//
// Automatically generated by addcopyright.py at 04/03/2012
* follows:
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* permission to link this library with independent modules to produce an
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package com.xensource.xenapi;
import java.net.URL;
import java.util.Map;
import java.util.TimeZone;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfig;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.client.XmlRpcHttpClientConfig;
import org.apache.xmlrpc.client.XmlRpcSun15HttpTransportFactory;
import com.xensource.xenapi.Types.BadServerResponse;
import com.xensource.xenapi.Types.SessionAuthenticationFailed;
import com.xensource.xenapi.Types.XenAPIException;
/**
* Represents a connection to a XenServer. Creating a new instance of this class initialises a new XmlRpcClient that is
* then used by all method calls: each method call in xenapi takes a Connection as a parameter, composes an XMLRPC
* method call, and dispatches it on the Connection's client via the dispatch method.
*/
public class Connection
{
/**
* The version of the bindings that this class belongs to.
*/
public static final String BINDINGS_VERSION = "5.6.100-1";
/**
* true if the connection is to the Rio edition of XenServer. Certain function calls are not allowed.
*
* @deprecated Use getAPIVersion() instead.
*/
@Deprecated
public Boolean rioConnection = false;
private APIVersion apiVersion;
protected int _wait = 600;
/**
* Updated when Session.login_with_password() is called.
*/
public APIVersion getAPIVersion()
{
return apiVersion;
}
/**
* The opaque reference to the session used by this connection
*/
private String sessionReference;
/**
* As seen by the xmlrpc library. From our point of view it's a server.
*/
private final XmlRpcClient client;
private final boolean deprecatedConstructorUsed;
/**
* Creates a connection to a particular server using a given username and password. This object can then be passed
* in to any other API calls.
*
* This constructor calls Session.loginWithPassword, passing itself as the first parameter.
*
* When this constructor is used, a call to dispose() (also called in the Connection's finalizer) will attempt a
* Session.logout on this connection.
*
* @deprecated Use a constructor that takes a URL as the first parameter instead.
*/
@Deprecated
public Connection(String client, String username, String password) throws java.net.MalformedURLException,
XmlRpcException, BadServerResponse, SessionAuthenticationFailed, XenAPIException
{
deprecatedConstructorUsed = true;
// To login normally we call login_with_password(username, password, "1.X"). 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
final String ApiVersion = APIVersion.latest().toString();
this.client = getClientFromURL(new URL(client));
try
{
//first try to login the modern way
this.sessionReference = loginWithPassword(this.client, username, password, ApiVersion);
} catch (BadServerResponse e)
{
//oops, something went wrong
String[] errDesc = e.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 == errDesc[0].compareTo("MESSAGE_PARAMETER_COUNT_MISMATCH")
&& 0 == errDesc[1].compareTo("session.login_with_password")
&& 0 == errDesc[2].compareTo("2")
&& 0 == 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;
}
}
try
{
setAPIVersion(new Session(sessionReference));
}
catch (XenAPIException exn)
{
dispose();
throw exn;
}
catch (XmlRpcException exn)
{
dispose();
throw exn;
}
}
/**
* Creates a connection to a particular server using a given username and password. This object can then be passed
* in to any other API calls.
*
* Note this constructor does NOT call Session.loginWithPassword; the programmer is responsible for calling it,
* passing the Connection as a parameter. No attempt to connect to the server is made until login is called.
*
* When this constructor is used, a call to dispose() will do nothing. The programmer is responsible for manually
* logging out the Session.
*/
public Connection(URL url, int wait)
{
deprecatedConstructorUsed = false;
_wait = wait;
this.client = getClientFromURL(url);
}
/**
* Creates a connection to a particular server using a given username and password. This object can then be passed
* in to any other API calls.
*
* The additional sessionReference parameter must be a reference to a logged-in Session. Any method calls on this
* Connection will use it. This constructor does not call Session.loginWithPassword, and dispose() on the resulting
* Connection object does not call Session.logout. The programmer is responsible for ensuring the Session is logged
* in and out correctly.
*/
public Connection(URL url, String sessionReference)
{
deprecatedConstructorUsed = false;
this.client = getClientFromURL(url);
this.sessionReference = sessionReference;
}
protected void finalize() throws Throwable
{
dispose();
super.finalize();
}
/**
* Nothrow guarantee.
*/
public void dispose()
{
if (!deprecatedConstructorUsed)
{
// We only need to do the Session.logout if they used the old deprecated constructor.
return;
}
try
{
if (sessionReference != null)
{
String method_call = "session.logout";
Object[] method_params = { Marshalling.toXMLRPC(this.sessionReference) };
client.execute(method_call, method_params);
sessionReference = null;
}
}
catch (XmlRpcException exn)
{
}
}
/**
* @deprecated The programmer is now responsible for calling login/logout themselves.
*/
@Deprecated
private static String loginWithPassword(XmlRpcClient client, String username, String password)
throws BadServerResponse, XmlRpcException, 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 SessionAuthenticationFailed();
}
}
throw new BadServerResponse(response);
}
/**
* @deprecated The programmer is now responsible for calling login/logout themselves.
*/
@Deprecated
private static String loginWithPassword(XmlRpcClient client, String username, String password, String ApiVersion)
throws BadServerResponse, XmlRpcException, 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 SessionAuthenticationFailed();
}
}
throw new BadServerResponse(response);
}
private XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
public XmlRpcClientConfigImpl getConfig()
{
return config;
}
protected XmlRpcClient getClientFromURL(URL url)
{
config.setTimeZone(TimeZone.getTimeZone("UTC"));
config.setServerURL(url);
config.setReplyTimeout(_wait * 1000);
config.setConnectionTimeout(5000);
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
* Changed to public to allow easier integration with HTTP-level streaming interface,
* see CA-15447
*/
public String getSessionReference()
{
return this.sessionReference;
}
/**
* The (auto-generated parts of) the bindings dispatch XMLRPC calls on this Connection's client through this method.
*/
protected Map dispatch(String method_call, Object[] method_params) throws XmlRpcException, XenAPIException
{
Map response = (Map) client.execute(method_call, method_params);
if (!deprecatedConstructorUsed)
{
// We are using the new-style constructor which doesn't perform login.
// Set this Connection's Session reference from the value returned on the wire.
if (method_call.equals("session.login_with_password") &&
response.get("Status").equals("Success"))
{
// Store the Session reference and ask the server what the
// API version it's using is.
Session session = Types.toSession(response.get("Value"));
sessionReference = session.ref;
setAPIVersion(session);
}
else if (method_call.equals("session.slave_local_login_with_password") &&
response.get("Status").equals("Success"))
{
// Store the Session reference and assume API version 1.2.
sessionReference = Types.toSession(response.get("Value")).ref;
apiVersion = APIVersion.API_1_2;
}
else if (method_call.equals("session.logout"))
{
// Work around a bug in XenServer 5.0 and below.
// session.login_with_password should have rejected us with
// HOST_IS_SLAVE, but instead we don't find out until later.
// We don't want to leak the session, so we need to log out
// this session from the master instead.
if (response.get("Status").equals("Failure"))
{
Object[] error = (Object[]) response.get("ErrorDescription");
if (error.length == 2 && error[0].equals("HOST_IS_SLAVE"))
{
try
{
URL client_url =
((XmlRpcHttpClientConfig)client.getClientConfig()).getServerURL();
Connection tmp_conn =
new Connection(new URL(client_url.getProtocol(),
(String)error[1],
client_url.getPort(),
client_url.getFile()), _wait);
tmp_conn.sessionReference = sessionReference;
try
{
Session.logout(tmp_conn);
}
finally
{
tmp_conn.dispose();
}
}
catch (Exception exn2)
{
// Ignore -- we're going to throw HostIsSlave anyway.
}
}
}
// Clear the stored Session reference.
this.sessionReference = null;
}
}
return Types.checkResponse(response);
}
private void setAPIVersion(Session session) throws XenAPIException, XmlRpcException
{
try
{
long major = session.getThisHost(this).getAPIVersionMajor(this);
long minor = session.getThisHost(this).getAPIVersionMinor(this);
apiVersion = APIVersion.fromMajorMinor(major, minor);
}
catch (BadServerResponse exn)
{
apiVersion = APIVersion.API_1_1;
}
}
}