All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xensource.xenapi.Connection Maven / Gradle / Ivy

There is a newer version: 6.2.0-3.1
Show newest version
/*
 * Copyright (c) 2007-2009 Citrix Systems, Inc.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation, with the additional linking exception as
 * follows:
 * 
 *   Linking this library statically or dynamically with other modules is
 *   making a combined work based on this library. Thus, the terms and
 *   conditions of the GNU General Public License cover the whole combination.
 * 
 *   As a special exception, the copyright holders of this library give you
 *   permission to link this library with independent modules to produce an
 *   executable, regardless of the license terms of these independent modules,
 *   and to copy and distribute the resulting executable under terms of your
 *   choice, provided that you also meet, for each linked independent module,
 *   the terms and conditions of the license of that module. An independent
 *   module is a module which is not derived from or based on this library. If
 *   you modify this library, you may extend this exception to your version of
 *   the library, but you are not obligated to do so. If you do not wish to do
 *   so, delete this exception statement from your version.
 * 
 * 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 General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package com.xensource.xenapi;

import java.net.URL;
import java.util.Map;

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 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.0.0-3";
    
    /**
     * 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;

    /**
     * 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)
    {
        deprecatedConstructorUsed = false;

        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 static XmlRpcClient getClientFromURL(URL url)
    {
        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
     * 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.
     */
    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()));
                            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;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy