org.apache.brooklyn.entity.brooklynnode.BrooklynEntityMirrorImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.brooklyn.entity.brooklynnode.BrooklynEntityMirrorImpl.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.entity.brooklynnode;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Callable;

import javax.annotation.Nullable;

import org.apache.brooklyn.api.effector.Effector;
import org.apache.brooklyn.core.effector.EffectorBody;
import org.apache.brooklyn.core.entity.AbstractEntity;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.entity.EntityDynamicType;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.feed.http.HttpFeed;
import org.apache.brooklyn.feed.http.HttpPollConfig;
import org.apache.brooklyn.util.collections.Jsonya;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.core.config.ConfigBag;
import org.apache.brooklyn.util.http.HttpToolResponse;
import org.apache.brooklyn.util.core.task.DynamicTasks;
import org.apache.brooklyn.util.core.task.Tasks;
import org.apache.brooklyn.util.net.Urls;
import org.apache.http.HttpStatus;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.net.MediaType;
import com.google.gson.Gson;

public class BrooklynEntityMirrorImpl extends AbstractEntity implements BrooklynEntityMirror {
    @SuppressWarnings("rawtypes")
    private class MirrorSummary implements Function<HttpToolResponse, Map> {
        @Override
        public Map apply(HttpToolResponse input) {
            Map<?, ?> entitySummary = new Gson().fromJson(input.getContentAsString(), Map.class);
            String catalogItemId = (String) entitySummary.get("catalogItemId");
            sensors().set(MIRROR_CATALOG_ITEM_ID, catalogItemId);
            return entitySummary;
        }
    }

    private HttpFeed mirror;

    //Passively mirror entity's state
    @Override
    protected void initEnrichers() {
    }

    @Override
    public void init() {
        super.init();
        connectSensorsAsync();

        //start spinning, could take some time before MIRRORED_ENTITY_URL is available for first time mirroring
        sensors().set(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STARTING);
    }

    @Override
    public void rebind() {
        super.rebind();
        connectSensorsAsync();
    }

    protected void connectSensorsAsync() {
        Callable<Void> asyncTask = new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                //blocks until available (could be a task)
                String mirroredEntityUrl = getConfig(MIRRORED_ENTITY_URL);
                Preconditions.checkNotNull(mirroredEntityUrl, "Required config: " + MIRRORED_ENTITY_URL);

                connectSensors(mirroredEntityUrl);
                return null;
            }
        };

        DynamicTasks
                .queueIfPossible(
                        Tasks.<Void>builder().displayName("Start entity mirror feed").body(asyncTask).build())
                .orSubmitAsync(this);
    }

    protected void connectSensors(String mirroredEntityUrl) {
        Function<HttpToolResponse, Void> mirrorSensors = new Function<HttpToolResponse, Void>() {
            @SuppressWarnings("rawtypes")
            @Override
            public Void apply(HttpToolResponse input) {
                Map sensors = new Gson().fromJson(input.getContentAsString(), Map.class);
                for (Object kv : sensors.entrySet())
                    sensors().set(Sensors.newSensor(Object.class, "" + ((Map.Entry) kv).getKey()),
                            ((Map.Entry) kv).getValue());
                sensors().set(MIRROR_STATUS, "normal");
                return null;
            }
        };

        final BrooklynEntityMirrorImpl self = this;
        mirror = HttpFeed.builder().entity(this).baseUri(mirroredEntityUrl)
                .credentialsIfNotNull(getConfig(BrooklynNode.MANAGEMENT_USER),
                        getConfig(BrooklynNode.MANAGEMENT_PASSWORD))
                .period(getConfig(POLL_PERIOD)).poll(HttpPollConfig.forMultiple().suburl("/sensors/current-state")
                        .onSuccess(mirrorSensors).onFailureOrException(new Function<Object, Void>() {
                            @Override
                            public Void apply(Object input) {
                                ServiceStateLogic.updateMapSensorEntry(self, Attributes.SERVICE_PROBLEMS,
                                        "mirror-feed", "error contacting service");
                                if (input instanceof HttpToolResponse) {
                                    int responseCode = ((HttpToolResponse) input).getResponseCode();
                                    if (responseCode == HttpStatus.SC_NOT_FOUND) {
                                        //the remote entity no longer exists
                                        Entities.unmanage(self);
                                    }
                                }
                                return null;
                            }
                        }))
                .poll(HttpPollConfig.forSensor(MIRROR_SUMMARY).onSuccess(new MirrorSummary())).build();

        populateEffectors();
    }

    private void populateEffectors() {
        HttpToolResponse result = http().get("/effectors");
        Collection<?> cfgEffectors = new Gson().fromJson(result.getContentAsString(), Collection.class);
        Collection<Effector<String>> remoteEntityEffectors = RemoteEffectorBuilder.of(cfgEffectors);
        EntityDynamicType mutableEntityType = getMutableEntityType();
        for (Effector<String> eff : remoteEntityEffectors) {
            //remote already started
            if ("start".equals(eff.getName()))
                continue;
            mutableEntityType.addEffector(eff);
        }
    }

    protected void disconnectSensors() {
        if (mirror != null)
            mirror.stop();
    }

    @Override
    public void destroy() {
        disconnectSensors();
    }

    @Override
    public EntityHttpClient http() {
        return new EntityHttpClientImpl(this, MIRRORED_ENTITY_URL);
    }

    public static class RemoteEffector<T> extends EffectorBody<T> {
        public final String remoteEffectorName;
        public final Function<HttpToolResponse, T> resultParser;

        /** creates an effector implementation which POSTs to a remote effector endpoint, optionally converting
         * the byte[] response (if resultParser is null then null is returned) */
        public RemoteEffector(String remoteEffectorName, @Nullable Function<HttpToolResponse, T> resultParser) {
            this.remoteEffectorName = remoteEffectorName;
            this.resultParser = resultParser;
        }

        @Override
        public T call(ConfigBag parameters) {
            MutableMap<String, String> headers = MutableMap.of(com.google.common.net.HttpHeaders.CONTENT_TYPE,
                    MediaType.JSON_UTF_8.toString());
            byte[] httpBody = Jsonya.of(parameters.getAllConfig()).toString().getBytes();
            String effectorUrl = Urls.mergePaths("effectors", Urls.encode(remoteEffectorName));
            HttpToolResponse result = ((BrooklynEntityMirror) entity()).http().post(effectorUrl, headers, httpBody);
            if (resultParser != null)
                return resultParser.apply(result);
            else
                return null;
        }
    }
}