com.terremark.impl.TerremarkClientImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.terremark.impl.TerremarkClientImpl.java

Source

/**
 * Copyright 2012 Terremark Worldwide Inc.
 *
 * 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 com.terremark.impl;

import java.net.MalformedURLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TreeMap;

import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;

import org.apache.commons.lang3.StringUtils;
import org.apache.wink.client.ClientConfig;
import org.apache.wink.client.RestClient;
import org.apache.wink.client.handlers.ClientHandler;
import org.apache.wink.client.httpclient.ApacheHttpClientConfig;

import com.terremark.ComputePoolHandler;
import com.terremark.EnvironmentHandler;
import com.terremark.NetworkHandler;
import com.terremark.OrganizationHandler;
import com.terremark.TerremarkClient;
import com.terremark.api.SupportedVersions;
import com.terremark.api.Time;
import com.terremark.api.VersionInfo;
import com.terremark.config.ContentType;
import com.terremark.config.PropertiesBuilder;
import com.terremark.config.Version;
import com.terremark.exception.InternalServerException;
import com.terremark.exception.TerremarkException;
import com.terremark.handlers.BasicAuthenticationHandler;
import com.terremark.handlers.CloudApiAuthenticationHandler;
import com.terremark.handlers.CustomHeadersHandler;
import com.terremark.handlers.HTTPLoggingHandler;
import com.terremark.impl.jaxrs.TerremarkJAXBJsonProvider;
import com.terremark.impl.jaxrs.TerremarkJAXBXmlProvider;

/**
 * Terremark Java API client implementation. This is responsible for creating the rest client from the provided
 * properties. It automatically gets and validates the supported versions. The server time is retrieved upon the
 * creation of the rest client and the clock drift between the server and the client is calculated. Various handlers are
 * made available to expose the API calls.
 *
 * @author <a href="mailto:spasam@terremark.com">Seshu Pasam</a>
 */
public final class TerremarkClientImpl implements TerremarkClient {
    /** Rest client */
    private final RestClient client;
    /** Compute pools handler */
    private final ComputePoolHandler cpHandler;
    /** Environment handler */
    private final EnvironmentHandler envHandler;
    /** Network handler */
    private final NetworkHandler netHandler;
    /** Organization handler */
    private final OrganizationHandler orgHandler;
    /** Client properties */
    private final ClientConfiguration properties;
    /** Version and date provider */
    private final VersionAndDateProvider versionAndDateProvider;

    /**
     * Default constructor. Creates the rest client and handlers. Get the server time and API versions. Validates the
     * client API version (if one is specified)
     *
     * @param builder Client properties.
     * @throws TerremarkException If error occurs while retrieving the server time and/or API versions.
     * @throws MalformedURLException If the API end point URL is malformed.
     */
    public TerremarkClientImpl(final PropertiesBuilder builder) throws TerremarkException, MalformedURLException {
        this.versionAndDateProvider = new VersionAndDateProvider();
        this.properties = builder.build();
        this.client = getClient();

        this.orgHandler = new OrganizationHandlerImpl(this.client, properties);
        this.envHandler = new EnvironmentHandlerImpl(this.client, properties);
        this.cpHandler = new ComputePoolHandlerImpl(this.client, properties);
        this.netHandler = new NetworkHandlerImpl(this.client, properties);

        initialize(properties.getVersion());
    }

    /**
     * Internal method to retrieve the server supported API versions and time. If client specified a specific API
     * version, it is checked to see if the server supports it. {@link java.lang.IllegalArgumentException} is thrown if
     * the client API version is not supported by the server. If client did not specify a version, the latest version in
     * the server provided list is chosen for the client. The time difference between the server and the client is
     * computed and cached.
     *
     * @param clientVersion Client specified API version. Can be null.
     * @throws TerremarkException If error occurs while retrieving the server time and/or API versions.
     */
    private void initialize(final Version clientVersion) throws TerremarkException {
        final SupportedVersions versions = getSupportedVersions();
        final TreeMap<Date, String> versionDates = new TreeMap<Date, String>();
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());

        for (final VersionInfo vi : versions.getVersionInfos()) {
            try {
                versionDates.put(sdf.parse(vi.getVersion()), vi.getVersion());
            } catch (final ParseException ex) {
                throw new InternalServerException(
                        "While validating the API version, got invalid version from server: " + vi.getVersion(),
                        ex);
            }
        }

        String clientVersionStr = clientVersion == null ? null : clientVersion.toString();
        if (clientVersionStr == null) {
            clientVersionStr = versionDates.lastEntry().getValue();
        } else {
            if (!versionDates.values().contains(clientVersionStr)) {
                throw new IllegalArgumentException("Invalid API version: " + clientVersionStr);
            }
        }

        final Date serverDateTime = getTime().getCurrentServerTime().toGregorianCalendar().getTime();

        versionAndDateProvider.setVersion(clientVersionStr);
        versionAndDateProvider.calculateClockSkew(serverDateTime);
    }

    /**
     * Returns the rest client instance. Configures the proxy, handlers and applications.
     *
     * @return Rest client.
     */
    private RestClient getClient() {
        final ClientConfig config = new ApacheHttpClientConfig(properties.getHttpClient());

        setProxy(config, properties);
        setHandlers(config, properties);
        setApplications(config, properties);

        return new RestClient(config);
    }

    /**
     * Configures the handlers as necessary. One of Cloud API or basic authentication handler is added. Custom headers
     * handler (for setting the API version and current date/time) and logging handlers are also added.
     *
     * @param config Apache Wink client configuration.
     * @param properties Terremark API configuration.
     */
    private void setHandlers(final ClientConfig config, final ClientConfiguration properties) {
        ClientHandler authHandler;
        if (StringUtils.isEmpty(properties.getAccessKey())) {
            authHandler = new BasicAuthenticationHandler(properties.getUserName(), properties.getPassword());
        } else {
            authHandler = new CloudApiAuthenticationHandler(properties.getAccessKey(), properties.getPrivateKey(),
                    properties.getSignatureAlgorithm());
        }

        config.handlers(new CustomHeadersHandler(versionAndDateProvider), authHandler, new HTTPLoggingHandler());
    }

    /**
     * Configures the application for the rest client. If the content type is XML, the custom JAXB XML provider is
     * added. If the content type is JSON, the custom JAXB JSON provider is added. These will aid in deserialization of
     * the responses.
     *
     * @param config Apache Wink client configuration.
     * @param properties Terremark API configuration.
     */
    private static void setApplications(final ClientConfig config, final ClientConfiguration properties) {
        config.applications(new Application() {
            @Override
            public Set<Object> getSingletons() {
                final ContentType contentType = properties.getContentType();
                final Set<Object> set = new HashSet<Object>();

                if (contentType == ContentType.XML) {
                    set.add(new TerremarkJAXBXmlProvider());
                } else if (contentType == ContentType.JSON) {
                    set.add(new TerremarkJAXBJsonProvider());
                } else {
                    throw new IllegalArgumentException("Invalid content type: " + contentType);
                }

                return set;
            }
        });
    }

    /**
     * Configures the proxy host and port, if necessary.
     *
     * @param config Apache Wink client configuration.
     * @param properties Terremark API configuration.
     */
    private static void setProxy(final ClientConfig config, final ClientConfiguration properties) {
        if (properties.getProxyHost() != null) {
            config.proxyHost(properties.getProxyHost());
            config.proxyPort(properties.getProxyPort());
        }
    }

    /**
     * Internally used only in this class to retrieve server date/time and supported versions.
     *
     * @param relativePath Relative path.
     * @param cls Expected response type.
     * @return Response.
     */
    private <T> T get(final String relativePath, final Class<T> cls) {
        // NOTE: No version check is performed here. So make sure only API calls that are supported by all versions are
        // using this method
        final String contentType = properties.getContentType() == ContentType.XML ? MediaType.APPLICATION_XML
                : MediaType.APPLICATION_JSON;
        return client.resource(properties.getUri() + relativePath).accept(contentType).get(cls);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ComputePoolHandler getComputePoolHandler() {
        return cpHandler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public EnvironmentHandler getEnvironmentHandler() {
        return envHandler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public NetworkHandler getNetworkHandler() {
        return netHandler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public OrganizationHandler getOrganizationHandler() {
        return orgHandler;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SupportedVersions getSupportedVersions() {
        return get("/versions/", SupportedVersions.class);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Time getTime() {
        return get("/time/", Time.class);
    }

    /**
     * This method should not be used by clients. This is exposed purely for testing purposes.
     *
     * @return Version and date provider.
     */
    public VersionAndDateProvider getVersionAndDateProvider() {
        return versionAndDateProvider;
    }
}