com.ibm.mqlight.api.impl.endpoint.BluemixEndpointService.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.mqlight.api.impl.endpoint.BluemixEndpointService.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 com.ibm.mqlight.api.impl.endpoint;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.ibm.mqlight.api.ClientException;
import com.ibm.mqlight.api.ClientRuntimeException;
import com.ibm.mqlight.api.endpoint.Endpoint;
import com.ibm.mqlight.api.endpoint.EndpointPromise;
import com.ibm.mqlight.api.impl.LogbackLogging;
import com.ibm.mqlight.api.logging.Logger;
import com.ibm.mqlight.api.logging.LoggerFactory;

public class BluemixEndpointService extends EndpointServiceImpl {

    private static final Logger logger = LoggerFactory.getLogger(BluemixEndpointService.class);

    static {
        LogbackLogging.setup();
    }

    private static final int THREAD_POOL_CORE_THREADS = Integer
            .getInteger("com.ibm.mqlight.BluemixEndpointService.coreThreads", 5);
    private static final int THREAD_POOL_MAX_THREADS = Integer
            .getInteger("com.ibm.mqlight.BluemixEndpointService.maxThreads", 5);
    private static final long THREAD_POOL_KEEP_ALIVE_SECONDS = Integer
            .getInteger("com.ibm.mqlight.BluemixEndpointService.keepAliveSeconds", 5);
    static {
        logger.data("clinit>", new Object[] { "THREAD_POOL_CORE_THREADS: ", THREAD_POOL_CORE_THREADS });
        logger.data("clinit>", new Object[] { "THREAD_POOL_CORE_THREADS: ", THREAD_POOL_MAX_THREADS });
        logger.data("clinit>", new Object[] { "THREAD_POOL_CORE_THREADS: ", THREAD_POOL_KEEP_ALIVE_SECONDS });
        if (THREAD_POOL_CORE_THREADS <= 0 || THREAD_POOL_CORE_THREADS > THREAD_POOL_MAX_THREADS) {
            throw new ClientRuntimeException("Invalid value (" + THREAD_POOL_CORE_THREADS
                    + ") specified for System property com.ibm.mqlight.BluemixEndpointService.coreThreads (must be > 0 and < the value specified for "
                    + "System property com.ibm.mqlight.BluemixEndpointService.maxThreads, or 5 when com.ibm.mqlight.BluemixEndpointService.maxThreads is not defined.");
        }
        if (THREAD_POOL_KEEP_ALIVE_SECONDS < 0) {
            throw new ClientRuntimeException("Invalid value (" + THREAD_POOL_KEEP_ALIVE_SECONDS
                    + ")specified for System property com.ibm.mqlight.BluemixEndpointService.keepAliveSeconds (must be >= 0).");
        }
    }

    private static ThreadPoolExecutor executor;

    private static class State {
        String lookupUri;
        int retryCount;
        String user;
        String password;
        LinkedList<Endpoint> endpoints;
        int nextEndpointIndex;
    }

    private final State state = new State();

    private static class BluemixThreadFactory implements ThreadFactory {
        private final ThreadGroup group;
        private final AtomicInteger number = new AtomicInteger();

        protected BluemixThreadFactory() {
            SecurityManager sm = System.getSecurityManager();
            group = sm == null ? Thread.currentThread().getThreadGroup() : sm.getThreadGroup();
        }

        @Override
        public Thread newThread(Runnable runnable) {
            Thread result = new Thread(group, runnable, "bluemix-endpoint-" + number.getAndIncrement());
            return result;
        }
    }

    protected String getVcapServices() {
        return System.getenv("VCAP_SERVICES");
    }

    protected String hitUri(String httpUri) throws IOException {
        final String methodName = "hitUri";
        logger.entry(this, methodName, httpUri);

        URL url = new URL(httpUri);
        InputStream in = url.openStream();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte buffer[] = new byte[1024];
        while (true) {
            int amount = in.read(buffer);
            if (amount < 0)
                break;
            out.write(buffer, 0, amount);
        }

        final String result = out.toString("UTF-8");

        logger.exit(this, methodName, result);

        return result;
    }

    protected void doHttpLookup(final String httpUri, final EndpointPromise future) {
        final String methodName = "doHttpLookup";
        logger.entry(this, methodName, httpUri, future);

        synchronized (this) {
            if (executor == null) {
                executor = new ThreadPoolExecutor(THREAD_POOL_CORE_THREADS, THREAD_POOL_MAX_THREADS,
                        THREAD_POOL_KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
                        new BluemixThreadFactory());
            }
        }

        executor.execute(new Runnable() {
            public void run() {
                final String methodName = "run";
                logger.entry(this, methodName);

                try {
                    String serviceJson = hitUri(httpUri);

                    JsonParser parser = new JsonParser();
                    JsonObject root = parser.parse(serviceJson).getAsJsonObject();
                    JsonArray services = root.get("service").getAsJsonArray();
                    Endpoint endpoint = null;
                    synchronized (this) {
                        state.endpoints = new LinkedList<>();
                        for (JsonElement serviceElement : services) {
                            String uri = serviceElement.getAsString();
                            state.endpoints.add(new EndpointImpl(uri, state.user, state.password));
                        }

                        if (state.endpoints.isEmpty()) {
                            state.endpoints = null;
                            state.nextEndpointIndex = 0;
                        } else {
                            endpoint = state.endpoints.get(0);
                            state.nextEndpointIndex = 1;
                        }
                    }

                    if (endpoint == null) {
                        doRetry(future);
                    } else {
                        future.setSuccess(endpoint);
                    }
                } catch (IOException ioException) {
                    logger.data(this, methodName, "will retry due to java.io.IOException exception",
                            ioException.getLocalizedMessage());
                    // Retry later...
                    doRetry(future);
                } catch (JsonParseException parseException) {
                    final ClientException exception = new ClientException(
                            "Could not parse the JSON returned by the IBM MQ Light Bluemix lookup service.  See linked exception for more information",
                            parseException);
                    logger.data(this, methodName, exception);
                    future.setFailure(exception);
                } catch (IllegalArgumentException iae) {
                    final ClientException exception = new ClientException(
                            "Endpoint information returned by IBM MQ Light Bluemix lookup service was not valid.  See linked exception for more information",
                            iae);
                    logger.data(this, methodName, exception);
                    future.setFailure(exception);
                }

                logger.exit(this, methodName);
            }
        });

        logger.exit(this, methodName);
    }

    protected void doRetry(EndpointPromise future) {
        final String methodName = "doRetry";
        logger.entry(this, methodName, future);

        int retry;
        synchronized (state) {
            retry = state.retryCount;
            ++state.retryCount;
        }
        future.setWait(calculateDelay(retry));

        logger.exit(this, methodName);
    }

    @Override
    public void lookup(EndpointPromise future) {
        final String methodName = "lookup";
        logger.entry(this, methodName, future);

        try {
            String lookupUri = null;
            Endpoint endpoint = null;
            boolean retry = false;

            synchronized (state) {
                if (state.lookupUri == null) {
                    String vcapServices = getVcapServices();
                    if (vcapServices != null) {

                        JsonParser parser = new JsonParser();
                        JsonObject root = parser.parse(vcapServices).getAsJsonObject();
                        for (Map.Entry<String, JsonElement> entry : root.entrySet()) {
                            if (entry.getKey().startsWith("mqlight")) {
                                JsonObject mqlight = entry.getValue().getAsJsonArray().get(0).getAsJsonObject();
                                JsonObject credentials = mqlight.get("credentials").getAsJsonObject();
                                state.user = credentials.get("username").getAsString();
                                state.lookupUri = credentials.get("connectionLookupURI").getAsString();
                                state.password = credentials.get("password").getAsString();
                                break;
                            }
                        }
                    }
                }

                lookupUri = state.lookupUri;
                if (state.lookupUri != null) {
                    if (state.endpoints == null) {
                        doHttpLookup(state.lookupUri, future);
                    } else if (state.nextEndpointIndex >= state.endpoints.size()) {
                        state.endpoints = null;
                        retry = true;
                    } else {
                        endpoint = state.endpoints.get(state.nextEndpointIndex++);
                    }
                }
            }

            if (lookupUri == null) {
                final ClientException exception = new ClientException(
                        "Could not locate a valid IBM Bluemix VCAP_SERVICES environment variable. Check 'service' parameter to NonBlockingClient.create(...) method.");
                logger.data(this, methodName, exception);
                future.setFailure(exception);
            } else if (retry) {
                doRetry(future);
            } else if (endpoint != null) {
                future.setSuccess(endpoint);
            }
        } catch (JsonParseException e) {
            // Can't parse VCAP_SERVICES values
            final ClientException exception = new ClientException(
                    "Could not parse the JSON present in the IBM Bluemix VCAP_SERVICES environment variable.  See linked exception for more information",
                    e);
            logger.data(this, methodName, exception);
            future.setFailure(exception);
        }

        logger.exit(this, methodName);
    }

    @Override
    public void onSuccess(Endpoint endpoint) {
        final String methodName = "onSuccess";
        logger.entry(this, methodName, endpoint);

        synchronized (state) {
            int index = -1;
            if (state.endpoints != null) {
                index = state.endpoints.indexOf(endpoint);
            }

            if (index == 0) {
                state.nextEndpointIndex = 0;
            } else if (index > 0) {
                // Shuffle to front
                state.endpoints.remove(index);
                state.endpoints.addFirst(endpoint);
                state.nextEndpointIndex = 0;
            }
        }

        logger.exit(this, methodName);
    }

}