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

com.ibm.mqlight.api.impl.endpoint.BluemixEndpointService Maven / Gradle / Ivy

There is a newer version: 1.0.2016062300
Show newest version
/**
 * 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 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 getConnectionUriKey() {
        return (System.getenv("MQLIGHT_JAVA_BLUEMIX_DISABLE_TLS") == null) ? "connectionLookupURI" : "nonTLSConnectionLookupURI";
    }

    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(), 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 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(getConnectionUriKey()).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);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy