org.apache.brooklyn.rest.client.BrooklynApi.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.rest.client.BrooklynApi.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.brooklyn.rest.client;

import static com.google.common.base.Preconditions.checkNotNull;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;

import javax.annotation.Nullable;
import javax.ws.rs.core.Response;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.jboss.resteasy.client.ClientExecutor;
import org.jboss.resteasy.client.ClientResponse;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
import org.jboss.resteasy.specimpl.BuiltResponse;
import org.jboss.resteasy.util.GenericType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;

import org.apache.brooklyn.rest.api.AccessApi;
import org.apache.brooklyn.rest.api.ActivityApi;
import org.apache.brooklyn.rest.api.ApplicationApi;
import org.apache.brooklyn.rest.api.CatalogApi;
import org.apache.brooklyn.rest.api.EffectorApi;
import org.apache.brooklyn.rest.api.EntityApi;
import org.apache.brooklyn.rest.api.EntityConfigApi;
import org.apache.brooklyn.rest.api.LocationApi;
import org.apache.brooklyn.rest.api.PolicyApi;
import org.apache.brooklyn.rest.api.PolicyConfigApi;
import org.apache.brooklyn.rest.api.ScriptApi;
import org.apache.brooklyn.rest.api.SensorApi;
import org.apache.brooklyn.rest.api.ServerApi;
import org.apache.brooklyn.rest.api.UsageApi;
import org.apache.brooklyn.rest.api.VersionApi;
import org.apache.brooklyn.rest.client.util.http.BuiltResponsePreservingError;
import org.apache.brooklyn.util.exceptions.Exceptions;

import io.swagger.annotations.ApiOperation;

/**
 * @author Adam Lowe
 */
@SuppressWarnings("deprecation")
public class BrooklynApi {

    private final String target;
    private final ClientExecutor clientExecutor;
    private final int maxPoolSize;
    private final int timeOutInMillis;
    private static final Logger LOG = LoggerFactory.getLogger(BrooklynApi.class);

    /**
     * @deprecated since 0.9.0. Use {@link BrooklynApi#newInstance(String)} instead
     */
    @Deprecated
    public BrooklynApi(URL endpoint) {
        this(checkNotNull(endpoint, "endpoint").toString());
    }

    /**
     * @deprecated since 0.9.0. Use {@link BrooklynApi#newInstance(String)} instead
     */
    @Deprecated
    public BrooklynApi(String endpoint) {
        // username/password cannot be null, but credentials can
        this(endpoint, null);
    }

    /**
     * @deprecated since 0.9.0. Use {@link BrooklynApi#newInstance(String, String, String)} instead
     */
    @Deprecated
    public BrooklynApi(URL endpoint, String username, String password) {
        this(endpoint.toString(), new UsernamePasswordCredentials(username, password));
    }

    /**
     * @deprecated since 0.9.0. Use {@link BrooklynApi#newInstance(String, String, String)} instead
     */
    @Deprecated
    public BrooklynApi(String endpoint, String username, String password) {
        this(endpoint, new UsernamePasswordCredentials(username, password));
    }

    /**
     * @deprecated since 0.9.0. Use {@link BrooklynApi#newInstance(String, String, String)} instead
     */
    @Deprecated
    public BrooklynApi(URL endpoint, @Nullable Credentials credentials) {
        this(endpoint.toString(), credentials);
    }

    /**
     * Creates a BrooklynApi using an HTTP connection pool
     *
     * @deprecated since 0.9.0. Use {@link BrooklynApi#newInstance(String, String, String)} instead
     */
    @Deprecated
    public BrooklynApi(String endpoint, @Nullable Credentials credentials) {
        this(endpoint, credentials, 20, 5000);
    }

    /**
     * Creates a BrooklynApi using a custom ClientExecutor
     *
     * @param endpoint the Brooklyn endpoint
     * @param clientExecutor the ClientExecutor
     * @see #getClientExecutor(org.apache.http.auth.Credentials)
     */
    public BrooklynApi(URL endpoint, ClientExecutor clientExecutor) {
        this.target = checkNotNull(endpoint, "endpoint").toString();
        this.maxPoolSize = -1;
        this.timeOutInMillis = -1;
        this.clientExecutor = checkNotNull(clientExecutor, "clientExecutor");
    }

    /**
     * Creates a BrooklynApi using an HTTP connection pool
     *
     * @param endpoint the Brooklyn endpoint
     * @param credentials user credentials or null
     * @param maxPoolSize maximum pool size
     * @param timeOutInMillis connection timeout in milliseconds
     */
    public BrooklynApi(String endpoint, @Nullable Credentials credentials, int maxPoolSize, int timeOutInMillis) {
        try {
            new URL(checkNotNull(endpoint, "endpoint"));
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException(e);
        }
        this.target = endpoint;
        this.maxPoolSize = maxPoolSize;
        this.timeOutInMillis = timeOutInMillis;
        this.clientExecutor = getClientExecutor(credentials);
    }

    private Supplier<PoolingHttpClientConnectionManager> connectionManagerSupplier = Suppliers
            .memoize(new Supplier<PoolingHttpClientConnectionManager>() {
                @Override
                public PoolingHttpClientConnectionManager get() {
                    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
                    connManager.setMaxTotal(maxPoolSize);
                    connManager.setDefaultMaxPerRoute(maxPoolSize);
                    return connManager;
                }
            });

    private Supplier<RequestConfig> reqConfSupplier = Suppliers.memoize(new Supplier<RequestConfig>() {
        @Override
        public RequestConfig get() {
            return RequestConfig.custom().setConnectTimeout(timeOutInMillis)
                    .setConnectionRequestTimeout(timeOutInMillis).build();
        }
    });

    /**
     * Creates a ClientExecutor for this BrooklynApi
     */
    protected ClientExecutor getClientExecutor(Credentials credentials) {
        CredentialsProvider provider = new BasicCredentialsProvider();
        if (credentials != null)
            provider.setCredentials(AuthScope.ANY, credentials);

        CloseableHttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(provider)
                .setDefaultRequestConfig(reqConfSupplier.get())
                .setConnectionManager(connectionManagerSupplier.get()).build();

        return new ApacheHttpClient4Executor(httpClient);
    }

    /**
     * Creates a BrooklynApi using an HTTP connection pool
     *
     * @param endpoint the Brooklyn endpoint
     * @return a new BrooklynApi instance
     */
    public static BrooklynApi newInstance(String endpoint) {
        return new BrooklynApi(endpoint, null);
    }

    /**
     * Creates a BrooklynApi using an HTTP connection pool
     *
     * @param endpoint the Brooklyn endpoint
     * @param maxPoolSize maximum connection pool size
     * @param timeOutInMillis connection timeout in millisecond
     * @return a new BrooklynApi instance
     */
    public static BrooklynApi newInstance(String endpoint, int maxPoolSize, int timeOutInMillis) {
        return new BrooklynApi(endpoint, null, maxPoolSize, timeOutInMillis);
    }

    /**
     * Creates a BrooklynApi using an HTTP connection pool
     *
     * @param endpoint the Brooklyn endpoint
     * @param username for authentication
     * @param password for authentication
     * @return a new BrooklynApi instance
     */
    public static BrooklynApi newInstance(String endpoint, String username, String password) {
        return new BrooklynApi(endpoint, new UsernamePasswordCredentials(username, password));
    }

    /**
     * Creates a BrooklynApi using an HTTP connection pool
     *
     * @param endpoint the Brooklyn endpoint
     * @param username for authentication
     * @param password for authentication
     * @param maxPoolSize maximum connection pool size
     * @param timeOutInMillis connection timeout in millisecond
     * @return a new BrooklynApi instance
     */
    public static BrooklynApi newInstance(String endpoint, String username, String password, int maxPoolSize,
            int timeOutInMillis) {
        return new BrooklynApi(endpoint, new UsernamePasswordCredentials(username, password), maxPoolSize,
                timeOutInMillis);
    }

    @SuppressWarnings("unchecked")
    private <T> T proxy(Class<T> clazz) {
        final T result0 = ProxyFactory.create(clazz, target, clientExecutor);
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz },
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            Object result1 = method.invoke(result0, args);
                            Class<?> type = String.class;
                            if (result1 instanceof Response) {
                                Response resp = (Response) result1;
                                if (isStatusCodeHealthy(resp.getStatus())
                                        && method.isAnnotationPresent(ApiOperation.class)) {
                                    type = getClassFromMethodAnnotationOrDefault(method, type);
                                }
                                // wrap the original response so it self-closes
                                result1 = BuiltResponsePreservingError.copyResponseAndClose(resp, type);
                            }
                            return result1;
                        } catch (Throwable e) {
                            if (e instanceof InvocationTargetException) {
                                // throw the original exception
                                e = ((InvocationTargetException) e).getTargetException();
                            }
                            throw Exceptions.propagate(e);
                        }
                    }

                    private boolean isStatusCodeHealthy(int code) {
                        return (code >= 200 && code <= 299);
                    }

                    private Class<?> getClassFromMethodAnnotationOrDefault(Method method, Class<?> def) {
                        Class<?> type;
                        try {
                            type = method.getAnnotation(ApiOperation.class).response();
                        } catch (Exception e) {
                            type = def;
                            LOG.debug("Unable to get class from annotation: {}.  Defaulting to {}", e.getMessage(),
                                    def.getName());
                            Exceptions.propagateIfFatal(e);
                        }
                        return type;
                    }
                });
    }

    public ActivityApi getActivityApi() {
        return proxy(ActivityApi.class);
    }

    public ApplicationApi getApplicationApi() {
        return proxy(ApplicationApi.class);
    }

    public CatalogApi getCatalogApi() {
        return proxy(CatalogApi.class);
    }

    public EffectorApi getEffectorApi() {
        return proxy(EffectorApi.class);
    }

    public EntityConfigApi getEntityConfigApi() {
        return proxy(EntityConfigApi.class);
    }

    public EntityApi getEntityApi() {
        return proxy(EntityApi.class);
    }

    public LocationApi getLocationApi() {
        return proxy(LocationApi.class);
    }

    public PolicyConfigApi getPolicyConfigApi() {
        return proxy(PolicyConfigApi.class);
    }

    public PolicyApi getPolicyApi() {
        return proxy(PolicyApi.class);
    }

    public ScriptApi getScriptApi() {
        return proxy(ScriptApi.class);
    }

    public SensorApi getSensorApi() {
        return proxy(SensorApi.class);
    }

    public ServerApi getServerApi() {
        return proxy(ServerApi.class);
    }

    public UsageApi getUsageApi() {
        return proxy(UsageApi.class);
    }

    public VersionApi getVersionApi() {
        return proxy(VersionApi.class);
    }

    public AccessApi getAccessApi() {
        return proxy(AccessApi.class);
    }

    public static <T> T getEntity(Response response, Class<T> type) {
        if (response instanceof ClientResponse) {
            ClientResponse<?> clientResponse = (ClientResponse<?>) response;
            return clientResponse.getEntity(type);
        } else if (response instanceof BuiltResponse) {
            // Handle BuiltResponsePreservingError turning objects into Strings
            if (response.getEntity() instanceof String && !type.equals(String.class)) {
                return new Gson().fromJson(response.getEntity().toString(), type);
            }
        }
        // Last-gasp attempt.
        return type.cast(response.getEntity());
    }

    public static <T> T getEntity(Response response, GenericType<T> type) {
        if (response instanceof ClientResponse) {
            ClientResponse<?> clientResponse = (ClientResponse<?>) response;
            return clientResponse.getEntity(type);
        } else if (response instanceof BuiltResponse) {
            // Handle BuiltResponsePreservingError turning objects into Strings
            if (response.getEntity() instanceof String) {
                return new Gson().fromJson(response.getEntity().toString(), type.getGenericType());
            }
        }
        // Last-gasp attempt.
        return type.getType().cast(response.getEntity());
    }

    /**
     * @deprecated since 0.8.0-incubating. Use {@link #getEntity(Response, GenericType)} instead.
     */
    @Deprecated
    public static <T> T getEntityGeneric(Response response, GenericType<T> type) {
        return getEntity(response, type);
    }

}