Package com.fasterxml.clustermate.dw

Source Code of com.fasterxml.clustermate.dw.DWBasedService

package com.fasterxml.clustermate.dw;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.bundles.AssetsBundle;
import com.yammer.dropwizard.config.Environment;
import com.yammer.dropwizard.lifecycle.Managed;

import com.fasterxml.storemate.shared.*;
import com.fasterxml.storemate.store.StorableStore;
import com.fasterxml.storemate.store.backend.StoreBackend;
import com.fasterxml.storemate.store.backend.StoreBackendBuilder;
import com.fasterxml.storemate.store.backend.StoreBackendConfig;
import com.fasterxml.storemate.store.file.FileManager;
import com.fasterxml.storemate.store.impl.StorableStoreImpl;

import com.fasterxml.clustermate.api.RequestPathBuilder;
import com.fasterxml.clustermate.jaxrs.IndexResource;
import com.fasterxml.clustermate.service.SharedServiceStuff;
import com.fasterxml.clustermate.service.Stores;
import com.fasterxml.clustermate.service.VManaged;
import com.fasterxml.clustermate.service.cfg.ServiceConfig;
import com.fasterxml.clustermate.service.cleanup.CleanerUpper;
import com.fasterxml.clustermate.service.cluster.ClusterBootstrapper;
import com.fasterxml.clustermate.service.cluster.ClusterInfoHandler;
import com.fasterxml.clustermate.service.cluster.ClusterViewByServer;
import com.fasterxml.clustermate.service.servlet.NodeStatusServlet;
import com.fasterxml.clustermate.service.servlet.ServiceDispatchServlet;
import com.fasterxml.clustermate.service.servlet.StoreEntryServlet;
import com.fasterxml.clustermate.service.servlet.SyncListServlet;
import com.fasterxml.clustermate.service.servlet.SyncPullServlet;
import com.fasterxml.clustermate.service.store.StoreHandler;
import com.fasterxml.clustermate.service.store.StoredEntry;
import com.fasterxml.clustermate.service.store.StoredEntryConverter;
import com.fasterxml.clustermate.service.store.StoresImpl;
import com.fasterxml.clustermate.service.sync.SyncHandler;
import com.fasterxml.clustermate.std.JdkHttpClientPathBuilder;

public abstract class DWBasedService<
    K extends EntryKey,
    E extends StoredEntry<K>,
    SCONFIG extends ServiceConfig,
    CONF extends DWConfigBase<SCONFIG, CONF>
>
    extends Service<CONF>
    implements Managed
{
    private final Logger LOG = LoggerFactory.getLogger(getClass());

    /**
     * List of {@link VManaged} objects we will dispatch start/stop calls to.
     */
    protected List<VManaged> _managed = Collections.emptyList();
   
    /**
     * Marker flag used to indicate cases when service is run in test
     * mode; propagated through configuration.
     */
    protected final boolean _testMode;

    /**
     * We need to keep track of things created, to let tests access
     * the information
     */
    protected SharedServiceStuff _serviceStuff;
   
    /**
     * Container for various stores we use for data, metadata.
     */
    protected StoresImpl<K,E> _stores;
   
    /**
     * This object is needed to allow test code to work around usual
     * waiting time restrictions.
     */
    protected final TimeMaster _timeMaster;

    /**
     * And we better hang on to cluster view as well
     */
    protected ClusterViewByServer _cluster;
   
    /**
     * Manager object that deals with data expiration and related
     * clean up tasks.
     */
    protected CleanerUpper<K,E> _cleanerUpper;

    /*
    /**********************************************************************
    /* Handlers
    /**********************************************************************
     */
   
    protected StoreHandler<K,E> _storeHandler;
   
    /*
    /**********************************************************************
    /* Construction
    /**********************************************************************
     */

    protected DWBasedService(String name, TimeMaster timings)
    {
        this(name, timings, false);
    }

    protected DWBasedService(String name, TimeMaster timings, boolean testMode)
    {
        super(name);
        _timeMaster = timings;
        _testMode = testMode;

        // Static stuff from under /html (except for root  level things
        // like /index.html that need special handling)
        addBundle(new AssetsBundle("/html"));
    }

    /*
    /**********************************************************************
    /* Life-cycle
    /**********************************************************************
     */

    @Override
    protected void initialize(CONF dwConfig,
            Environment environment) throws IOException
    {
        // first things first: we need to get start()/stop() calls, so:
        environment.manage(this);

        final SCONFIG config = dwConfig.getServiceConfig();
       
        _managed = new ArrayList<VManaged>();

        StoredEntryConverter<K,E> entryConverter = constructEntryConverter(config, environment);
        FileManager files = constructFileManager(config);

        _serviceStuff = constructServiceStuff(config, _timeMaster,
               entryConverter, files);
        if (_testMode) {
            _serviceStuff.markAsTest();
        }
       
        /* Let's try opening up BDB-JE: must have been created,
         * and have tables we expect; otherwise we'll fail right away.
         */
        LOG.info("Trying open Stores (StorableStore, node store, last-access store)");
         _stores = _constructStores(_serviceStuff);
         _managed.add(_stores);
        LOG.info("Opened Stores successfully");
        _stores.initAndOpen(false);

        // Then: read in cluster information (config file, BDB):
        final int port = dwConfig.getHttpConfiguration().getPort();
        LOG.info("Initializing cluster configuration (port {})...", port);
        final long startTime = _timeMaster.currentTimeMillis();
        _cluster = new ClusterBootstrapper<K,E>(startTime, _serviceStuff, _stores)
            .bootstrap(port);
        _managed.add(_cluster);
    
        LOG.info("Cluster configuration setup complete, with {} nodes", _cluster.size());
       
        // Index page must be done via resource, otherwise will conflict with DW/JAX-RS Servlet:
        environment.addResource(new IndexResource(loadResource("/index.html"), loadResource("/favicon.jpg")));

        // Let's first construct handlers we use:
        LOG.info("Creating handlers for service endpoints");
        ClusterInfoHandler nodeH = constructClusterInfoHandler(_serviceStuff, _cluster);
        SyncHandler<K,E> syncH = constructSyncHandler(_serviceStuff, _stores, _cluster);
        _storeHandler = constructStoreHandler(_serviceStuff, _stores);
       
        LOG.info("Adding service end points");
        addServiceEndpoints(_serviceStuff, environment,
                nodeH, syncH, _storeHandler);

        LOG.info("Adding health checks");
        addHealthChecks(_serviceStuff, environment);

        LOG.info("Initializing background cleaner tasks");
        _cleanerUpper = new CleanerUpper<K,E>(_serviceStuff, _stores, _cluster);
        _managed.add(_cleanerUpper);
       
        LOG.info("Initialization complete: HTTP service now running on port {}",
                dwConfig.getHttpConfiguration().getPort());
    }

    /*
    /**********************************************************************
    /* Factory methods: basic config objects
    /**********************************************************************
     */
   
    /**
     * Overridable method that is used for getting helper object used for
     * constructing {@link StoredEntry} instances to store in the
     * entry metadata store (currently BDB).
     * Default implementation delegates to {@link VagabondDWConfig}, to
     * allow for configuration overrides
     */
    protected abstract StoredEntryConverter<K,E> constructEntryConverter(SCONFIG config,
            Environment environment);

    protected abstract FileManager constructFileManager(SCONFIG serviceConfig);

    protected abstract StoresImpl<K,E> constructStores(SharedServiceStuff stuff,
            SCONFIG serviceConfig, StorableStore store);   

    protected abstract SharedServiceStuff constructServiceStuff(SCONFIG serviceConfig,
            TimeMaster timeMaster, StoredEntryConverter<K,E> entryConverter,
            FileManager files);
   
    /*
    /**********************************************************************
    /* Factory methods for constructing handlers
    /**********************************************************************
     */

    protected abstract StoreHandler<K,E> constructStoreHandler(SharedServiceStuff serviceStuff,
            Stores<K,E> stores);


    protected SyncHandler<K,E> constructSyncHandler(SharedServiceStuff stuff,
            StoresImpl<K,E> stores, ClusterViewByServer cluster)
    {
        return new SyncHandler<K,E>(_serviceStuff, _stores, _cluster);
    }

    protected ClusterInfoHandler constructClusterInfoHandler(SharedServiceStuff stuff,
            ClusterViewByServer cluster)
    {
        return new ClusterInfoHandler(stuff, cluster);
    }

    /*
    /**********************************************************************
    /* Factory methods: servlets
    /**********************************************************************
     */

    protected abstract StoreEntryServlet<K,E> constructStoreEntryServlet(SharedServiceStuff serviceStuff,
            ClusterViewByServer cluster, StoreHandler<K,E> storeHandler);

    protected NodeStatusServlet constructNodeStatusServlet(ClusterInfoHandler nodeHandler) {
        return new NodeStatusServlet(nodeHandler);
    }

    protected SyncListServlet<K,E> constructSyncListServlet(SharedServiceStuff stuff,
            ClusterViewByServer cluster, SyncHandler<K,E> syncHandler) {
        return new SyncListServlet<K,E>(stuff, cluster, syncHandler);
    }

    protected SyncPullServlet<K,E> constructSyncPullServlet(SharedServiceStuff stuff,
            ClusterViewByServer cluster, SyncHandler<K,E> syncHandler) {
        return new SyncPullServlet<K,E>(stuff, cluster, syncHandler);
    }
   
    /*
    /**********************************************************************
    /* Methods for service end point additions
    /**********************************************************************
     */

    /**
     * Method called to create service endpoints, given set of
     * handlers.
     */
    protected void addServiceEndpoints(SharedServiceStuff stuff,
            Environment environment,
            ClusterInfoHandler nodeHandler, SyncHandler<K,E> syncHandler,
            StoreHandler<K,E> storeHandler)
    {
        final ClusterViewByServer cluster = syncHandler.getCluster();
        NodeStatusServlet nodeStatusServlet = constructNodeStatusServlet(nodeHandler);
        SyncListServlet<K,E> syncListServlet = constructSyncListServlet(
                stuff, cluster, syncHandler);
        SyncPullServlet<K,E> syncPullServlet = constructSyncPullServlet(
                stuff, cluster, syncHandler);
        StoreEntryServlet<K,E> storeEntryServlet = constructStoreEntryServlet(stuff,
                cluster, storeHandler);
       
        ServiceDispatchServlet<K,E> dispatcher = new ServiceDispatchServlet<K,E>(
                cluster, stuff,
                nodeStatusServlet, storeEntryServlet, null,
                syncListServlet, syncPullServlet);

        RequestPathBuilder rootBuilder = rootPath(stuff.getServiceConfig());
        String rootPath = servletPath(rootBuilder);
        LOG.info("Registering main Dispatcher servlet at: {}", rootPath);
        environment.addServlet(dispatcher, rootPath);

        // // And finally servlet for for entry access
       
        addStoreEntryServlet(stuff, environment, cluster, _storeHandler);

    }

    /*
     * Old single-Servlet implementation:
     */
    /*
    {
        final ServiceConfig config = stuff.getServiceConfig();
        final ClusterViewByServer cluster = syncHandler.getCluster();

        // All paths are dynamic, so we need a mapper:
        RequestPathStrategy pathStrategy = stuff.getPathStrategy();
        RequestPathBuilder pathBuilder;

        pathBuilder = pathStrategy.appendNodeStatusPath(rootPath(config));

        // // And then start by adding cluster-info (aka node status)
        // // and sync end points as plain servlets
       
        NodeStatusServlet nsServlet = constructNodeStatusServlet(nodeHandler);
        if (nsServlet != null) {
            environment.addServlet(nsServlet, servletPath(pathBuilder));
        }
   
        pathBuilder = pathStrategy.appendSyncListPath(rootPath(config));
        environment.addServlet(new SyncListServlet<K,E>(stuff, cluster, syncHandler),
                servletPath(pathBuilder));
        pathBuilder = pathStrategy.appendSyncPullPath(rootPath(config));
        environment.addServlet(new SyncPullServlet<K,E>(stuff, cluster, syncHandler),
                servletPath(pathBuilder));

        // // And finally servlet for for entry access
       
        addStoreEntryServlet(stuff, environment, pathStrategy, cluster, _storeHandler);
    }
    */
   
    protected void addStoreEntryServlet(SharedServiceStuff stuff,
            Environment environment,
            ClusterViewByServer cluster,
            StoreHandler<K,E> storeHandler)
    {
    }
   
    protected void addHealthChecks(SharedServiceStuff stuff,
            Environment environment) { }
   
    /*
    /**********************************************************************
    /* Internal methods
    /**********************************************************************
     */
   
    protected StoresImpl<K,E> _constructStores(SharedServiceStuff stuff)
        throws IOException
    {
        final SCONFIG v = stuff.getServiceConfig();
        StoreBackendBuilder<?> b = v.instantiateBackendBuilder();
        StoreBackendConfig backendConfig = v._storeBackendConfigOverride;
        if (backendConfig == null) { // no overrides, use databinding
            Class<? extends StoreBackendConfig> cfgType = b.getConfigClass();
            if (v.storeBackendConfig == null) {
                throw new IllegalStateException("Missing 'VagabondServiceConfig.storeBackendConfig");
            }
            backendConfig = stuff.convertValue(v.storeBackendConfig, cfgType);
        }
        StoreBackend backend = b.with(v.storeConfig)
                .with(backendConfig)
                .build();
        StorableStore store = new StorableStoreImpl(v.storeConfig, backend, _timeMaster,
               stuff.getFileManager());
        return constructStores(stuff, v, store);
    }

    @Override
    public void start() throws Exception
    {
        LOG.info("Starting up {} VManaged objects", _managed.size());
        for (VManaged managed : _managed) {
            LOG.info("Starting up: {}", managed.getClass().getName());
            managed.start();
        }
        // TODO Auto-generated method stub
        LOG.info("VManaged object startup complete");
       
    }

    @Override
    public void stop() throws Exception
    {
        int count = _managed.size();
        LOG.info("Stopping {} VManaged objects", _managed.size());
        while (--count >= 0) {
            VManaged managed = _managed.remove(count);
            String desc = managed.getClass().getName();
            try {
                LOG.info("Stopping: {}", desc);
                managed.stop();
            } catch (Exception e) {
                LOG.warn(String.format("Problems trying to stop VManaged of type %s: (%s) %s",
                        desc, e.getClass().getName(), e.getMessage()),
                        e);
            }
        }
        LOG.info("VManaged object shutdown complete");
    }

    /*
    /**********************************************************************
    /* Accessors for tests
    /**********************************************************************
     */

    public TimeMaster getTimeMaster() {
        return _timeMaster;
    }
   
    /*
    /**********************************************************************
    /* Helper methods
    /**********************************************************************
     */

    protected RequestPathBuilder rootPath(ServiceConfig config)
    {
        return new JdkHttpClientPathBuilder("localhost")
            .addPathSegments(config.servicePathRoot);
    }

    /**
     * Helper method for constructing Servlet registration path, given
     * a basic end point path definition. Currently just verifies prefix
     * and suffix slashes and adds '*' as necessary.
     */
    protected String servletPath(RequestPathBuilder pathBuilder)
    {
        String base = pathBuilder.getPath();
        if (!base.endsWith("*")) {
            if (base.endsWith("/")) {
                base += "*";
            } else {
                base += "/*";
            }
        }
        if (!base.startsWith("/")) {
            base = "/"+base;
        }
        return base;
    }

    protected byte[] loadResource(String ref) throws IOException
    {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream(4000);
        InputStream in = getClass().getResourceAsStream(ref);
        byte[] buffer = new byte[4000];
        int count;
    
        while ((count = in.read(buffer)) > 0) {
            bytes.write(buffer, 0, count);
        }
        in.close();
        byte[] data = bytes.toByteArray();
        if (data.length == 0) {
            String msg = "Could not find resource '"+ref+"'";
            LOG.error(msg);
            throw new IllegalArgumentException(msg);
        }
        return bytes.toByteArray();
    }
}
TOP

Related Classes of com.fasterxml.clustermate.dw.DWBasedService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.