org.kiji.rest.KijiRESTService.java Source code

Java tutorial

Introduction

Here is the source code for org.kiji.rest.KijiRESTService.java

Source

/**
 * (c) Copyright 2013 WibiData, Inc.
 *
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * 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.kiji.rest;

import java.io.IOException;
import java.util.Iterator;
import java.util.Set;

import javax.ws.rs.ext.ExceptionMapper;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;
import com.yammer.dropwizard.json.ObjectMapperFactory;
import org.eclipse.jetty.servlets.CrossOriginFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.kiji.delegation.Lookups;
import org.kiji.rest.health.KijiClientHealthCheck;
import org.kiji.rest.plugins.KijiRestPlugin;
import org.kiji.rest.representations.KijiRestEntityId;
import org.kiji.rest.representations.SchemaOption;
import org.kiji.rest.serializers.AvroToJsonStringSerializer;
import org.kiji.rest.serializers.JsonToKijiRestEntityId;
import org.kiji.rest.serializers.JsonToSchemaOption;
import org.kiji.rest.serializers.KijiRestEntityIdToJson;
import org.kiji.rest.serializers.SchemaOptionToJson;
import org.kiji.rest.serializers.TableLayoutToJsonSerializer;
import org.kiji.rest.serializers.Utf8ToJsonSerializer;
import org.kiji.rest.tasks.CloseTask;
import org.kiji.rest.tasks.RefreshInstancesTask;
import org.kiji.rest.tasks.ShutdownTask;
import org.kiji.schema.KijiURI;

/**
 * Service to provide REST access to a list of Kiji instances.
 * The configuration is parametrized by a file that contains the cluster
 * address and the list of instances.
 */
public class KijiRESTService extends Service<KijiRESTConfiguration> {

    private static final Logger LOG = LoggerFactory.getLogger(KijiRESTService.class);

    /**
     * Main method entry point into the KijiREST service.
     *
     * @param args The server token and the path to the YAML configuration file.
     * @throws Exception Prevents the REST service from starting.
     */
    public static void main(final String[] args) throws Exception {
        new KijiRESTService().run(args);
    }

    /** {@inheritDoc} */
    @Override
    public final void initialize(final Bootstrap<KijiRESTConfiguration> bootstrap) {
        bootstrap.setName("kiji-rest");

        // Initialize plugins.
        for (KijiRestPlugin plugin : Lookups.get(KijiRestPlugin.class)) {
            LOG.info("Initializing plugin {}", plugin.getClass());
            plugin.initialize(bootstrap);
        }
    }

    /**
     * {@inheritDoc}
     *
     * @throws IOException when instance in configuration can not be opened and closed.
     */
    @Override
    public final void run(final KijiRESTConfiguration configuration, final Environment environment)
            throws IOException {
        final KijiURI clusterURI = KijiURI.newBuilder(configuration.getClusterURI()).build();
        final ManagedKijiClient managedKijiClient = new ManagedKijiClient(configuration);
        environment.manage(managedKijiClient);

        // Setup the health checker for the KijiClient
        environment.addHealthCheck(new KijiClientHealthCheck(managedKijiClient));

        // Remove all built-in Dropwizard ExceptionHandler.
        // Always depend on custom ones.
        // Inspired by Jeremy Whitlock's suggestion on thoughtspark.org.
        Set<Object> jerseyResources = environment.getJerseyResourceConfig().getSingletons();
        Iterator<Object> jerseyResourcesIterator = jerseyResources.iterator();
        while (jerseyResourcesIterator.hasNext()) {
            Object jerseyResource = jerseyResourcesIterator.next();
            if (jerseyResource instanceof ExceptionMapper
                    && jerseyResource.getClass().getName().startsWith("com.yammer.dropwizard.jersey")) {
                jerseyResourcesIterator.remove();
            }
        }

        // Load admin task to manually refresh instances.
        environment.addTask(new RefreshInstancesTask(managedKijiClient));
        // Load admin task to manually close instances and tables.
        environment.addTask(new CloseTask(managedKijiClient));
        // Load admin task to manually shutdown the system.
        environment.addTask(new ShutdownTask(managedKijiClient, configuration));

        // Adds custom serializers.
        registerSerializers(environment.getObjectMapperFactory());

        // Adds exception mappers to print better exception messages to the client than what
        // Dropwizard does by default.
        environment.addProvider(new GeneralExceptionMapper());

        // Load resources.
        for (KijiRestPlugin plugin : Lookups.get(KijiRestPlugin.class)) {
            LOG.info("Loading plugin {}", plugin.getClass());
            plugin.install(managedKijiClient, configuration, environment);
        }

        // Allow global CORS filter. CORS off by default.
        if (configuration.getCORS()) {
            environment.addFilter(CrossOriginFilter.class, configuration.getHttpConfiguration().getRootPath());
            LOG.info("Global cross-origin resource sharing is allowed.");
        }
    }

    /**
     * Registers custom serializers with the Jackson ObjectMapper via DropWizard's
     * ObjectMapperFactory. This is used by both the service initialization and the test
     * setup method to ensure consistency between test and production.
     *
     * @param mapperFactory is the ObjectMapperFactory.
     */
    public static void registerSerializers(ObjectMapperFactory mapperFactory) {
        // TODO: Add a module to convert btw Avro's specific types and JSON. The default
        // mapping seems to throw an exception.
        SimpleModule module = new SimpleModule("KijiRestModule",
                new Version(1, 0, 0, null, "org.kiji.rest", "serializers"));
        module.addSerializer(new AvroToJsonStringSerializer());
        module.addSerializer(new Utf8ToJsonSerializer());
        module.addSerializer(new TableLayoutToJsonSerializer());
        module.addSerializer(new SchemaOptionToJson());
        module.addDeserializer(SchemaOption.class, new JsonToSchemaOption());
        module.addSerializer(new KijiRestEntityIdToJson());
        module.addDeserializer(KijiRestEntityId.class, new JsonToKijiRestEntityId());
        mapperFactory.registerModule(module);
    }
}