org.openbaton.plugin.PluginListener.java Source code

Java tutorial

Introduction

Here is the source code for org.openbaton.plugin.PluginListener.java

Source

/*
 * Copyright (c) 2015-2018 Open Baton (http://openbaton.org)
 *
 * 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 org.openbaton.plugin;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeoutException;
import org.apache.commons.codec.binary.Base64;
import org.openbaton.catalogue.nfvo.PluginAnswer;
import org.openbaton.catalogue.nfvo.images.BaseNfvImage;
import org.openbaton.catalogue.nfvo.networks.BaseNetwork;
import org.openbaton.catalogue.nfvo.viminstances.BaseVimInstance;
import org.openbaton.exceptions.NotFoundException;
import org.openbaton.nfvo.common.configuration.NfvoGsonDeserializerImage;
import org.openbaton.nfvo.common.configuration.NfvoGsonDeserializerNetwork;
import org.openbaton.nfvo.common.configuration.NfvoGsonDeserializerVimInstance;
import org.openbaton.nfvo.common.configuration.NfvoGsonSerializerVimInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PluginListener implements Runnable {

    private static final String exchange = "openbaton-exchange";
    private String pluginId;
    private Object pluginInstance;
    private Logger log;
    private Channel channel;
    private Gson gson = new GsonBuilder()
            .registerTypeHierarchyAdapter(byte[].class, new ByteArrayToBase64TypeAdapter())
            .registerTypeAdapter(BaseVimInstance.class, new NfvoGsonDeserializerVimInstance())
            .registerTypeAdapter(BaseNetwork.class, new NfvoGsonDeserializerNetwork())
            .registerTypeAdapter(BaseNfvImage.class, new NfvoGsonDeserializerImage())
            .registerTypeAdapter(BaseVimInstance.class, new NfvoGsonSerializerVimInstance()).setPrettyPrinting()
            .create();
    private String brokerIp;
    private int brokerPort;
    private String username;
    private String password;
    private String virtualHost;
    private Connection connection;
    private ThreadPoolExecutor executor;

    public void setDurable(boolean durable) {
        this.durable = durable;
    }

    private boolean durable = true;

    public void setPluginId(String pluginId) {
        this.pluginId = pluginId;
    }

    public void setPluginInstance(Object pluginInstance) {
        this.pluginInstance = pluginInstance;
        log = LoggerFactory.getLogger(pluginInstance.getClass().getName());
    }

    public void setExecutor(ThreadPoolExecutor executor) {
        this.executor = executor;
    }

    public ThreadPoolExecutor getExecutor() {
        return executor;
    }

    private static class ByteArrayToBase64TypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer<byte[]> {
        public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {
            return Base64.decodeBase64(json.getAsString());
        }

        public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
            return new JsonPrimitive(Base64.encodeBase64String(src));
        }
    }

    @Override
    public void run() {

        try {
            initRabbitMQ();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
            return;
        }
        try {

            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                        byte[] body) throws IOException {
                    AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
                            .correlationId(properties.getCorrelationId()).build();

                    executor.execute(() -> {
                        String message;
                        try {
                            message = new String(body, "UTF-8");
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                            return;
                        }
                        log.trace("Received message");
                        log.trace("Message content received: " + message);

                        PluginAnswer answer = new PluginAnswer();

                        try {
                            answer.setAnswer(executeMethod(message));
                        } catch (InvocationTargetException e) {
                            e.getTargetException().printStackTrace();
                            answer.setException(e.getTargetException());
                        } catch (Exception e) {
                            e.printStackTrace();
                            answer.setException(e);
                        }

                        String response;
                        try {
                            response = gson.toJson(answer);

                            log.trace("Answer is: " + response);
                            log.trace("Reply queue is: " + properties.getReplyTo());

                            channel.basicPublish(exchange, properties.getReplyTo(), props, response.getBytes());

                        } catch (Throwable e) {
                            e.printStackTrace();
                            answer.setException(e);
                            log.trace("Answer is: " + answer);
                            log.trace("Reply queue is: " + properties.getReplyTo());
                            try {
                                channel.basicPublish(exchange, properties.getReplyTo(), props,
                                        gson.toJson(answer).getBytes());

                            } catch (IOException ex) {
                                log.error(String.format("Thread %s got an exception: %s",
                                        Thread.currentThread().getName(), e.getMessage()));
                                e.printStackTrace();
                            }
                        }
                    });

                    channel.basicAck(envelope.getDeliveryTag(), false);
                    log.trace(String.format("Ack %d", envelope.getDeliveryTag()));

                    synchronized (this) {
                        this.notify();
                    }
                }
            };

            channel.basicConsume(pluginId, false, consumer);

            // Wait and be prepared to consume the message from RPC client.
            while (true) {
                synchronized (consumer) {
                    try {
                        consumer.wait();
                    } catch (InterruptedException e) {
                        log.info("Ctrl-c received");
                        System.exit(0);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (connection != null)
                try {
                    connection.close();
                } catch (IOException ignored) {
                }
        }

        try {
            if (channel == null || connection == null)
                System.exit(3);
            channel.close();
            connection.close();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
    }

    private Serializable executeMethod(String pluginMessageString)
            throws InvocationTargetException, IllegalAccessException, NotFoundException {

        JsonObject pluginMessageObject = gson.fromJson(pluginMessageString, JsonObject.class);

        List<Object> params = new ArrayList<>();

        for (JsonElement param : pluginMessageObject.get("parameters").getAsJsonArray()) {
            Object p = gson.fromJson(param, Object.class);
            if (p != null) {
                params.add(p);
            }
        }

        Class pluginClass = pluginInstance.getClass();

        log.trace("There are " + params.size() + " parameters");
        String methodName = pluginMessageObject.get("methodName").getAsString();
        log.trace("Looking for method: " + methodName);

        for (Method m : pluginClass.getMethods()) {
            log.trace(
                    "Method checking is: " + m.getName() + " with " + m.getParameterTypes().length + " parameters");
            byte[] avoid = new byte[0];
            if (m.getName().equals(methodName) && m.getParameterTypes().length == params.size()
                    && !m.getParameterTypes()[m.getParameterTypes().length - 1].getCanonicalName()
                            .equals(avoid.getClass().getCanonicalName())) {
                if (!m.getReturnType().equals(Void.class)) {
                    if (params.size() != 0) {
                        params = getParameters(pluginMessageObject.get("parameters").getAsJsonArray(),
                                m.getParameterTypes());
                        for (Object p : params) {
                            log.trace("param class is: " + p.getClass());
                        }
                        return (Serializable) m.invoke(pluginInstance, params.toArray());
                    } else {
                        return (Serializable) m.invoke(pluginInstance);
                    }
                } else {
                    if (params.size() != 0) {
                        params = getParameters(pluginMessageObject.get("parameters").getAsJsonArray(),
                                m.getParameterTypes());
                        for (Object p : params) {
                            log.trace("param class is: " + p.getClass());
                        }
                        m.invoke(pluginInstance, params.toArray());
                    } else {
                        m.invoke(pluginInstance);
                    }

                    return null;
                }
            }
        }

        throw new NotFoundException("method not found");
    }

    private List<Object> getParameters(JsonArray parameters, Class<?>[] parameterTypes) {
        List<Object> res = new LinkedList<>();
        for (int i = 0; i < parameters.size(); i++) {
            res.add(gson.fromJson(parameters.get(i), parameterTypes[i]));
        }
        return res;
    }

    private void initRabbitMQ() throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(brokerIp);
        factory.setPort(brokerPort);
        factory.setPassword(password);
        factory.setUsername(username);
        factory.setVirtualHost(virtualHost);

        connection = factory.newConnection();
        channel = connection.createChannel();

        channel.queueDeclare(pluginId, this.durable, false, true, null);
        channel.queueBind(pluginId, exchange, pluginId);

        channel.basicQos(1);
    }

    public void setBrokerIp(String brokerIp) {
        this.brokerIp = brokerIp;
    }

    public void setBrokerPort(int brokerPort) {
        this.brokerPort = brokerPort;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setVirtualHost(String virtualHost) {
        this.virtualHost = virtualHost;
    }
}