org.o3project.odenos.remoteobject.RemoteObject.java Source code

Java tutorial

Introduction

Here is the source code for org.o3project.odenos.remoteobject.RemoteObject.java

Source

/*
 * Copyright 2015 NEC Corporation.
 *
 * 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.o3project.odenos.remoteobject;

import org.apache.commons.lang.StringUtils;
import org.o3project.odenos.remoteobject.actor.Mail;
import org.o3project.odenos.remoteobject.event.EventSubscription;
import org.o3project.odenos.remoteobject.event.ObjectPropertyChanged;
import org.o3project.odenos.remoteobject.event.ObjectSettingsChanged;
import org.o3project.odenos.remoteobject.message.Event;
import org.o3project.odenos.remoteobject.message.MessageBodyUnpacker.ParseBodyException;
import org.o3project.odenos.remoteobject.message.Request;
import org.o3project.odenos.remoteobject.message.Response;
import org.o3project.odenos.remoteobject.messagingclient.MessageDispatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * The base class by which a remote calling is possible.
 *
 */
public class RemoteObject {
    private static final Logger log = LoggerFactory.getLogger(RemoteObject.class);

    protected MessageDispatcher messageDispatcher = null;
    protected ObjectProperty objectProperty = null;
    protected ObjectSettings objectSettings = new ObjectSettings();
    protected EventSubscription eventSubscription = null;

    protected final RequestParser<IActionCallback> parser;

    // mailbox to receive messages from MessageDispatcher.
    protected Queue<Mail> mailbox;
    // true if Actor is running on this object right now.
    protected boolean running = false;

    /**
     * Constructor.
     * @param objectId object ID.
     * @param dispatcher MessageDispatcher object.
     */
    public RemoteObject(String objectId, MessageDispatcher dispatcher) {
        this.objectProperty = new ObjectProperty(this.getClass().getSimpleName(), objectId);
        this.objectProperty.setObjectState(ObjectProperty.State.INITIALIZING);
        setMessageDispatcher(dispatcher);
        this.eventSubscription = new EventSubscription(objectId);

        this.parser = this.createParser();

        this.mailbox = new ConcurrentLinkedQueue<Mail>();
    }

    public boolean onInitialize(ObjectProperty prop) {
        return true;
    }

    /**
     * Runs finalization of the objects.
     */
    public void onFinalize() {
        ObjectProperty prev = (ObjectProperty) objectProperty.clone();
        if (objectProperty.setObjectState(ObjectProperty.State.FINALIZING) != ObjectProperty.State.FINALIZING) {
            onPropertyChanged(ObjectPropertyChanged.Action.delete.name(), prev, null);
        }
        messageDispatcher.removeLocalObject(this);
    }

    public String getObjectId() {
        return objectProperty.getObjectId();
    }

    public ObjectProperty getProperty() {
        return objectProperty;
    }

    public ObjectSettings getSettings() {
        return objectSettings;
    }

    public MessageDispatcher getMessageDispatcher() {
        return messageDispatcher;
    }

    /**
     * Set a state.
     * @param next string of state.
     */
    public void setState(String next) {
        if (this.objectProperty.getObjectState().equals(next)) {
            return;
        }
        ObjectProperty prev = (ObjectProperty) objectProperty.clone();
        this.objectProperty.setObjectState(next);
        ObjectProperty curr = (ObjectProperty) objectProperty.clone();

        onPropertyChanged(ObjectPropertyChanged.Action.update.name(), prev, curr);
    }

    /**
     * Sets a Message Dispatcher.
     * @param messageDispatcher Message DIspatcher object.
     */
    public void setMessageDispatcher(MessageDispatcher messageDispatcher) {
        this.messageDispatcher = messageDispatcher;
        if (messageDispatcher != null) {
            this.messageDispatcher.addLocalObject(this);
        }
    }

    /**
     * Get ObjectId of SystemManager from MessageDispatcher.
     *
     * @return ObjectId of SystemManager
     */
    protected final String getSystemManagerId() {
        return this.messageDispatcher.getSystemManagerId();
    }

    /**
     * Get ObjectId of EventManager from MessageDispatcher.
     *
     * @return ObjectId of EventManager
     */
    protected final String getEventManagerId() {
        return this.messageDispatcher.getEventManagerId();
    }

    /**
     * Register information in order to message transport with RemoteObject to
     * MessageDispatcher.
     *
     * @param objectId ID of the registering RemoteObject
     * @throws IOException if an I/O error occurs.
     */
    protected final void addRemoteObject(final String objectId) throws IOException {
        this.messageDispatcher.addRemoteObject(objectId);
    }

    /**
     * Unregister RemoteObject from MessageDispatcher.
     *
     * @param objectId
     *            ID of the unregistering RemoteObject
     */
    protected final void removeRemoteObject(final String objectId) throws IOException {
        this.messageDispatcher.removeRemoteObject(objectId);
    }

    /**
     * Send a request to the specified RemoteObject asynchronously.
     *
     * @param objectId
     *            ID of the targeted RemoteObject
     * @param method
     *            method for the path
     * @param path
     *            path of the RemoteObject
     * @param body
     *            requested data
     * @return Future object to obtain the response of the request
     * @throws Exception if an error occurs.
     */
    protected Response requestSync(String objectId, Request.Method method, String path, Object body)
            throws Exception {
        return messageDispatcher.requestSync(new Request(objectId, method, path, body), this.getObjectId());
    }

    /**
     * Send a request to the specified RemoteObject and get the response of it.
     *
     * @param objectId
     *            ID of the targeted RemoteObject
     * @param method
     *            method for the path
     * @param path
     *            path of the RemoteObject
     * @param body
     *            requested data
     * @return a response of the request
     * @throws Exception if an error occurs.
     */
    public Response request(String objectId, Request.Method method, String path, Object body) throws Exception {
        return requestSync(objectId, method, path, body);
    }

    /**
     * Notify an event of this RemoteObject asynchronously.
     *
     * @param eventType
     *            type of Event to notify
     * @param body
     *            Event object
     * @throws Exception if an error occurs.
     */
    protected void publishEvent(String eventType, Object body) throws Exception {
        Event event = new Event(getObjectId(), eventType, body);
        messageDispatcher.publishEventAsync(event);
    }

    /**
     * Subscribe events of other RemoteObjects. Caller should configure
     * "eventSubscription" member to specify publisher IDs and events before
     * call this method.
     *
     * @return Future object
     * @throws Exception if an error occurs.
     */
    protected Response applyEventSubscription() throws Exception {
        return messageDispatcher.subscribeEvent(eventSubscription);
    }

    /**
     * Event handler to dispatch Request which is posted from another
     * RemoteObject.
     *
     * @param request
     *            the posted Request
     * @return response to the RemoteObject
     */
    public Response dispatchRequest(Request request) {
        log.debug("dispatchRequest: " + request.method + ", " + request.path);
        if (StringUtils.stripToNull(request.path) == null) {
            return new Response(Response.BAD_REQUEST, null);
        }

        RequestParser<IActionCallback>.ParsedRequest parsed = parser.parse(request);
        Response response = null;

        if (parsed == null) {
            response = onRequest(request);
        } else {
            IActionCallback callback = parsed.getResult();
            if (callback == null) {
                return new Response(Response.BAD_REQUEST, null);
            }
            try {
                response = callback.process(parsed);
            } catch (Exception e) {
                log.error("Exception Request: " + request.method + ", " + request.path);
                response = new Response(Response.BAD_REQUEST, null);
            }
        }
        if (response == null) {
            response = new Response(Response.BAD_REQUEST, null);
        }
        return response;
    }

    /**
     * Post Event to Dispatcher.
     * @param event event.
     */
    public void dispatchEvent(Event event) {
        if (eventSubscription.contains(event.publisherId, event.eventType)) {
            doPostEvent(event);
        }
    }

    /**
     * @return true is Actor is running on this object.
     */
    public boolean isRunning() {
        return running;
    }

    /**
     * @param running true if Actor is going to run on this object.
     */
    public void setRunning(boolean running) {
        this.running = running;
    }

    /**
     * @return mailbox.
     */
    public Queue<Mail> getMailbox() {
        return mailbox;
    }

    protected interface IActionCallback {
        Response process(RequestParser<IActionCallback>.ParsedRequest parser) throws Exception;
    }

    private RequestParser<IActionCallback> createParser() {
        return new RequestParser<IActionCallback>() {
            {
                addRule(Request.Method.GET, "property", new IActionCallback() {
                    @Override
                    public Response process(final RequestParser<IActionCallback>.ParsedRequest parsed) {
                        return doGetProperty();
                    }
                });
                addRule(Request.Method.PUT, "property", new IActionCallback() {
                    @Override
                    public Response process(final RequestParser<IActionCallback>.ParsedRequest parsed)
                            throws ParseBodyException {
                        return doPutProperty(parsed.getRequest().getBody(ObjectProperty.class));
                    }
                });
                addRule(Request.Method.GET, "settings", new IActionCallback() {
                    @Override
                    public Response process(final RequestParser<IActionCallback>.ParsedRequest parsed) {
                        return doGetSettings();
                    }
                });
                addRule(Request.Method.PUT, "settings", new IActionCallback() {
                    @Override
                    public Response process(final RequestParser<IActionCallback>.ParsedRequest parsed)
                            throws ParseBodyException {
                        return doPutSettings(parsed.getRequest().getBody(ObjectSettings.class));
                    }
                });
                addRule(Request.Method.POST, "event", new IActionCallback() {
                    @Override
                    public Response process(final RequestParser<IActionCallback>.ParsedRequest parsed)
                            throws ParseBodyException {
                        return doPostEvent(parsed.getRequest().getBody(Event.class));
                    }
                });
            }
        };
    }

    protected Response doGetProperty() {
        return new Response(Response.OK, objectProperty);
    }

    protected final Response doPutProperty(final ObjectProperty data) {
        if (isFinalize(data)) {
            onFinalize();
            return new Response(Response.OK, null);
        }

        ObjectProperty prev = (ObjectProperty) objectProperty.clone();
        if (prev.isModify(data)) {
            getProperty().putProperty(data);
            onPropertyChanged(ObjectPropertyChanged.Action.update.name(), prev, getProperty());
        }

        return doGetProperty();
    }

    protected final Response doGetSettings() {
        return new Response(Response.OK, getSettings());
    }

    protected final Response doPutSettings(final ObjectSettings data) {
        ObjectSettings prev = (ObjectSettings) getSettings().clone();
        if (prev.isModify(data)) {
            getSettings().putSettings(data);
            onSettingsChanged(ObjectSettingsChanged.Action.update.name(), prev, getSettings());
        }

        return doGetSettings();
    }

    protected Response doPostEvent(final Event event) {
        onEvent(event);
        return new Response(Response.ACCEPTED, null);
    }

    protected void onStateChanged(String oldState, String newState) {
        if (!ObjectProperty.State.FINALIZING.equals(oldState) && ObjectProperty.State.FINALIZING.equals(newState)) {
            onFinalize();
        }
    }

    protected final void onPropertyChanged(final String action, final ObjectProperty prev,
            final ObjectProperty curr) {
        ObjectPropertyChanged msg = new ObjectPropertyChanged(action, prev, curr);
        try {
            publishEvent(ObjectPropertyChanged.TYPE, msg);
        } catch (Exception e) {
            log.error("Failed to ObjectPropertyChanged.", e);
        }
    }

    protected void onSettingsChanged(final String action, final ObjectSettings prev, final ObjectSettings curr) {
        ObjectSettingsChanged msg = new ObjectSettingsChanged(action, prev, curr);
        try {
            publishEvent(ObjectSettingsChanged.TYPE, msg);
        } catch (Exception e) {
            log.error("Failed to ObjectSettingsChanged.", e);
        }
    }

    /**
     * Event handler to handle unknown Request which is posted from another
     * RemoteObject.
     *
     * @param request
     *            the posted Request
     * @return response to the RemoteObject
     */
    protected Response onRequest(Request request) {
        return new Response(Response.BAD_REQUEST, null);
    }

    /**
     * onEvent is handle event which is notified from other RemoteObject.
     *
     * @param event
     *            the notified Event
     */
    protected void onEvent(Event event) {
    }

    /**
     * ObjectProperty State Change check FINALIZING.
     *
     * @param prop
     *            new ObjectProperty
     * @return true if state is FINALIZING.
     *
     */
    private boolean isFinalize(final ObjectProperty prop) {
        if (prop.property.containsKey(ObjectProperty.PropertyNames.OBJECT_STATE)) {
            return prop.getObjectState().equals(ObjectProperty.State.FINALIZING);
        }
        return false;
    }

}