org.apache.lens.server.LensServices.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.lens.server.LensServices.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.lens.server;

import static org.apache.lens.server.api.LensConfConstants.*;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.concurrent.*;

import org.apache.lens.api.error.ErrorCollection;
import org.apache.lens.api.error.ErrorCollectionFactory;
import org.apache.lens.server.api.ServiceProvider;
import org.apache.lens.server.api.error.LensException;
import org.apache.lens.server.api.events.LensEventService;
import org.apache.lens.server.api.metrics.MetricsService;
import org.apache.lens.server.metrics.MetricsServiceImpl;
import org.apache.lens.server.model.LogSegregationContext;
import org.apache.lens.server.model.MappedDiagnosticLogSegregationContext;
import org.apache.lens.server.session.LensSessionImpl;
import org.apache.lens.server.stats.StatisticsService;
import org.apache.lens.server.user.UserConfigLoaderFactory;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hive.service.CompositeService;
import org.apache.hive.service.Service;
import org.apache.hive.service.cli.CLIService;

import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * Manage lifecycle of all Lens services
 */
@Slf4j
public class LensServices extends CompositeService implements ServiceProvider {

    /** The Constant LENS_SERVICES_NAME. */
    public static final String LENS_SERVICES_NAME = "lens_services";

    /** Constant for FileSystem auto close on shutdown config */
    private static final String FS_AUTOMATIC_CLOSE = "fs.automatic.close";
    private static final String FS_IO_FILE_BUFFER_SIZE = "io.file.buffer.size";

    /** The instance. */
    private static LensServices instance = new LensServices(LENS_SERVICES_NAME,
            new MappedDiagnosticLogSegregationContext());

    /** The conf. */
    private HiveConf conf;

    /** The cli service. */
    private CLIService cliService;

    /** The services. */
    private final Map<String, Service> services = new LinkedHashMap<String, Service>();

    /** The lens services. */
    private final List<BaseLensService> lensServices = new ArrayList<BaseLensService>();

    /** The persist dir. */
    private Path persistDir;

    /** The persistence file system. */
    private FileSystem persistenceFS;

    /** The stopping. */
    private boolean stopping = false;

    /**
     * The metrics service.
     */
    private MetricsService metricsService;

    /**
     * The Constant SERVER_STATE_PERSISTENCE_ERRORS.
     */
    public static final String SERVER_STATE_PERSISTENCE_ERRORS = "total-server-state-persistence-errors";

    /** The service mode. */
    @Getter
    @Setter
    private ServiceMode serviceMode;

    /** Scheduled Executor which persists the server state periodically*/
    private ScheduledExecutorService serverSnapshotScheduler;

    /* Lock for synchronizing persistence of LensServices state */
    private final Object statePersistenceLock = new Object();

    @Getter
    private ErrorCollection errorCollection;

    private boolean isServerStatePersistenceEnabled;

    private long serverStatePersistenceInterval;

    @Getter
    private final LogSegregationContext logSegregationContext;

    /**
     * Incr counter.
     *
     * @param counter the counter
     */
    private void incrCounter(String counter) {
        getMetricService().incrCounter(LensServices.class, counter);
    }

    /**
     * Gets counter value.
     *
     * @param counter the counter
     */
    private long getCounter(String counter) {
        return getMetricService().getCounter(LensServices.class, counter);
    }

    public static LensException processLensException(LensException exc) {
        if (exc != null) {
            exc.buildLensErrorTO(get().getErrorCollection());
        }
        return exc;
    }

    /**
     * The Enum ServiceMode.
     */
    public enum ServiceMode {

        /** The read only. */
        READ_ONLY, // All requests on sesssion resource and Only GET requests on all other resources
        /** The metastore readonly. */
        METASTORE_READONLY, // Only GET requests on metastore service and
        // all other requests on other services are accepted
        /** The metastore nodrop. */
        METASTORE_NODROP, // DELETE requests on metastore are not accepted
        /** The open. */
        OPEN // All requests are accepted
    }

    /**
     * Instantiates a new lens services.
     *
     * @param name the name
     */
    public LensServices(String name, @NonNull final LogSegregationContext logSegregationContext) {
        super(name);
        this.logSegregationContext = logSegregationContext;
    }

    // This is only for test, to simulate a restart of the server
    public static void setInstance(LensServices newInstance) {
        instance = newInstance;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.hive.service.CompositeService#init(org.apache.hadoop.hive.conf.HiveConf)
     */
    @SuppressWarnings("unchecked")
    @Override
    public synchronized void init(HiveConf hiveConf) {

        if (getServiceState() == STATE.NOTINITED) {

            initializeErrorCollection();
            conf = hiveConf;
            conf.setVar(HiveConf.ConfVars.HIVE_SESSION_IMPL_CLASSNAME, LensSessionImpl.class.getCanonicalName());
            serviceMode = conf.getEnum(SERVER_MODE, ServiceMode.valueOf(DEFAULT_SERVER_MODE));
            cliService = new CLIService(null);
            UserConfigLoaderFactory.init(conf);
            // Add default services
            addService(cliService);
            addService(new EventServiceImpl(LensEventService.NAME));
            addService(new MetricsServiceImpl(MetricsService.NAME));
            addService(new StatisticsService(StatisticsService.STATS_SVC_NAME));

            // Add configured services, these are instances of LensService which need a CLIService instance
            // for session management
            String[] serviceNames = conf.getStrings(SERVICE_NAMES);
            for (String sName : serviceNames) {
                try {

                    String serviceClassName = conf.get(getServiceImplConfKey(sName));

                    if (StringUtils.isBlank(serviceClassName)) {
                        log.warn("Invalid class for service {} class={}", sName, serviceClassName);
                        continue;
                    }

                    Class<?> cls = Class.forName(serviceClassName);

                    if (BaseLensService.class.isAssignableFrom(cls)) {
                        Class<? extends BaseLensService> serviceClass = (Class<? extends BaseLensService>) cls;
                        log.info("Adding {}  service with {}", sName, serviceClass);
                        Constructor<?> constructor = serviceClass.getConstructor(CLIService.class);
                        BaseLensService service = (BaseLensService) constructor.newInstance(cliService);
                        addService(service);
                        lensServices.add(service);
                    } else if (Service.class.isAssignableFrom(cls)) {
                        Class<? extends Service> serviceClass = (Class<? extends Service>) cls;
                        // Assuming default constructor
                        Service svc = serviceClass.newInstance();
                        addService(svc);
                    } else {
                        log.warn("Unsupported service class {} for service {}", serviceClassName, sName);
                    }
                } catch (Exception e) {
                    log.warn("Could not add service:{}", sName, e);
                    throw new RuntimeException("Could not add service:" + sName, e);
                }
            }

            for (Service svc : getServices()) {
                services.put(svc.getName(), svc);
            }

            // This will init all services in the order they were added
            super.init(conf);

            // setup persisted state
            isServerStatePersistenceEnabled = conf.getBoolean(SERVER_STATE_PERSISTENCE_ENABLED,
                    DEFAULT_SERVER_STATE_PERSISTENCE_ENABLED);
            if (isServerStatePersistenceEnabled) {
                String persistPathStr = conf.get(SERVER_STATE_PERSIST_LOCATION,
                        DEFAULT_SERVER_STATE_PERSIST_LOCATION);
                persistDir = new Path(persistPathStr);
                try {
                    Configuration configuration = new Configuration(conf);
                    configuration.setBoolean(FS_AUTOMATIC_CLOSE, false);

                    int outStreamBufferSize = conf.getInt(STATE_PERSIST_OUT_STREAM_BUFF_SIZE,
                            DEFAULT_STATE_PERSIST_OUT_STREAM_BUFF_SIZE);
                    configuration.setInt(FS_IO_FILE_BUFFER_SIZE, outStreamBufferSize);
                    log.info("STATE_PERSIST_OUT_STREAM_BUFF_SIZE IN BYTES:{}", outStreamBufferSize);
                    persistenceFS = FileSystem.newInstance(persistDir.toUri(), configuration);
                    setupPersistedState();
                } catch (Exception e) {
                    log.error("Could not recover from persisted state", e);
                    throw new RuntimeException("Could not recover from persisted state", e);
                }
                serverStatePersistenceInterval = conf.getLong(SERVER_STATE_PERSISTENCE_INTERVAL_MILLIS,
                        DEFAULT_SERVER_STATE_PERSISTENCE_INTERVAL_MILLIS);
            }
            log.info("Initialized services: {}", services.keySet().toString());
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.hive.service.CompositeService#start()
     */
    public synchronized void start() {
        if (getServiceState() != STATE.STARTED) {
            super.start();
        }

        if (!isServerStatePersistenceEnabled) {
            log.info("Server restart is not enabled. Not persisting lens server state");
        } else {
            ThreadFactory factory = new BasicThreadFactory.Builder()
                    .namingPattern("Lens-server-snapshotter-Thread-%d").daemon(true).priority(Thread.NORM_PRIORITY)
                    .build();
            serverSnapshotScheduler = Executors.newSingleThreadScheduledExecutor(factory);
            serverSnapshotScheduler.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    try {
                        final String runId = UUID.randomUUID().toString();
                        logSegregationContext.setLogSegregationId(runId);
                        persistLensServiceState();
                        log.info("SnapShot of Lens Services created");
                    } catch (Exception e) {
                        incrCounter(SERVER_STATE_PERSISTENCE_ERRORS);
                        log.error("Unable to persist lens server state", e);
                    }
                }
            }, serverStatePersistenceInterval, serverStatePersistenceInterval, TimeUnit.MILLISECONDS);
            log.info("Enabled periodic persistence of lens server state at {} millis interval",
                    serverStatePersistenceInterval);
        }
    }

    /**
     * Setup persisted state.
     *
     * @throws IOException            Signals that an I/O exception has occurred.
     * @throws ClassNotFoundException the class not found exception
     */
    private void setupPersistedState() throws IOException, ClassNotFoundException {
        for (BaseLensService service : lensServices) {
            ObjectInputStream in = null;
            Path path = getServicePersistPath(service);
            try {
                try {
                    in = new ObjectInputStream(persistenceFS.open(path));
                } catch (FileNotFoundException fe) {
                    log.warn("Persisted state not available for service: {} at: {}", service.getName(), path);
                    continue;
                }
                service.readExternal(in);
                log.info("Recovered service {} from persisted state {}", service.getName(), path);
            } finally {
                if (in != null) {
                    in.close();
                }
            }
        }
    }

    /**
     * Persist lens service state.
     */
    private void persistLensServiceState() {
        synchronized (statePersistenceLock) {
            log.info("Persisting server state in {}", persistDir);
            String now = "" + System.currentTimeMillis();
            for (BaseLensService service : lensServices) {
                try {
                    persistState(service, now);
                } catch (Exception e) {
                    incrCounter(SERVER_STATE_PERSISTENCE_ERRORS);
                    log.error("Error while persisting state for service {}", service.getName(), e);
                }
            }
        }
    }

    private void persistState(BaseLensService service, String time) throws IOException {
        log.info("Persisting state of service: {}", service.getName());
        Path serviceWritePath = new Path(persistDir, service.getName() + ".out" + "." + time);
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(persistenceFS.create(serviceWritePath));
            service.writeExternal(out);
        } finally {
            if (out != null) {
                out.close();
            }
        }
        Path servicePath = getServicePersistPath(service);
        if (persistenceFS.exists(servicePath)) {
            // delete the destination first, because rename is no-op in HDFS, if destination exists
            if (!persistenceFS.delete(servicePath, true)) {
                throw new IOException("Failed to delete " + servicePath);
            }
        }
        if (!persistenceFS.rename(serviceWritePath, servicePath)) {
            throw new IOException("Failed to rename " + serviceWritePath + " to " + servicePath);
        }
        log.info("Persisted service {} to [{}]", service.getName(), servicePath);
    }

    /**
     * Gets the service persist path.
     *
     * @param service the service
     * @return the service persist path
     */
    private Path getServicePersistPath(BaseLensService service) {
        return new Path(persistDir, service.getName() + ".final");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.hive.service.CompositeService#stop()
     */
    public synchronized void stop() {
        if (getServiceState() != STATE.STOPPED) {
            log.info("Stopping lens server");
            stopping = true;
            for (BaseLensService service : lensServices) {
                service.prepareStopping();
            }

            if (isServerStatePersistenceEnabled) {
                try {
                    //1. shutdown serverSnapshotScheduler gracefully by allowing already triggered task (if any) to finish
                    if (serverSnapshotScheduler != null) {
                        serverSnapshotScheduler.shutdown();
                        try { //Wait for shutdown. Shutdown should be immediate in case no task is running at this point
                            while (!serverSnapshotScheduler.awaitTermination(1, TimeUnit.MINUTES)) {
                                log.info("Waiting for Lens-server-snapshotter to shutdown gracefully...");
                            }
                        } catch (InterruptedException e) {
                            log.error("Lens-server-snapshotter interrupted while shutting down", e);
                        }
                        log.info("Lens-server-snapshotter was shutdown");
                    } else {
                        log.info("Lens-server-snapshotter wasn't started, so no need to shutdown");
                    }
                    //2. persist the latest state of all the services
                    persistLensServiceState();
                } finally {
                    try {
                        persistenceFS.close();
                        log.info("Persistence File system object close complete");
                    } catch (Exception e) {
                        log.error("Error while closing Persistence File system object", e);
                    }
                }
            }

            super.stop();

        }
    }

    public STATE getServiceState() {
        return super.getServiceState();
    }

    public boolean isStopping() {
        return stopping;
    }

    /**
     * Gets the.
     *
     * @return the lens services
     */
    public static LensServices get() {
        return instance;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.lens.server.api.ServiceProvider#getService(java.lang.String)
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T extends Service> T getService(String sName) {
        return (T) services.get(sName);
    }

    public List<BaseLensService> getLensServices() {
        return lensServices;
    }

    private void initializeErrorCollection() {
        try {
            errorCollection = new ErrorCollectionFactory().createErrorCollection();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Could not create error collection.", e);
        }
    }

    private MetricsService getMetricService() {
        if (metricsService == null) {
            metricsService = LensServices.get().getService(MetricsService.NAME);
            if (metricsService == null) {
                throw new NullPointerException("Could not get metrics service");
            }
        }
        return metricsService;
    }
}