Package org.neo4j.webadmin.rest

Source Code of org.neo4j.webadmin.rest.ConfigService

package org.neo4j.webadmin.rest;

import static org.neo4j.webadmin.rest.WebUtils.addHeaders;
import static org.neo4j.webadmin.rest.WebUtils.buildBadJsonExceptionResponse;
import static org.neo4j.webadmin.rest.WebUtils.buildExceptionResponse;
import static org.neo4j.webadmin.rest.WebUtils.dodgeStartingUnicodeMarker;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;

import javax.naming.OperationNotSupportedException;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;

import org.neo4j.rest.WebServerFactory;
import org.neo4j.rest.domain.DatabaseLocator;
import org.neo4j.rest.domain.JsonHelper;
import org.neo4j.rest.domain.JsonRenderers;
import org.neo4j.rest.domain.PropertyValueException;
import org.neo4j.webadmin.console.ConsoleSessions;
import org.neo4j.webadmin.domain.ConfigServiceRepresentation;
import org.neo4j.webadmin.domain.LifecycleRepresentation;
import org.neo4j.webadmin.domain.NoSuchPropertyException;
import org.neo4j.webadmin.domain.ServerPropertyRepresentation;
import org.neo4j.webadmin.properties.ServerConfiguration;
import org.neo4j.webadmin.task.DeferredTask;
import org.neo4j.webadmin.task.JvmRestartTask;

/**
* A web service that exposes various configuration settings for a running neo4j
* REST server.
*
* @author Jacob Hansson <jacob@voltvoodoo.com>
*
*/
@Path( ConfigService.ROOT_PATH )
public class ConfigService
{

    public static final String ROOT_PATH = "/server/config";
    public static final String ALL_SETTINGS_PATH = "/all";

    protected ServerConfiguration properties;

    //
    // CONSTRUCT
    //

    public ConfigService() throws IOException
    {
        properties = ServerConfiguration.getInstance();
    }

    @GET
    @Produces( MediaType.APPLICATION_JSON )
    public Response getServiceDefinition( @Context UriInfo uriInfo )
    {

        String entity = JsonRenderers.DEFAULT.render( new ConfigServiceRepresentation(
                uriInfo.getBaseUri() ) );

        return addHeaders(
                Response.ok( entity, JsonRenderers.DEFAULT.getMediaType() ) ).build();
    }

    //
    // PUBLIC
    //

    /**
     * Get a full list of available settings.
     */
    @GET
    @Produces( MediaType.APPLICATION_JSON )
    @Path( ALL_SETTINGS_PATH )
    public synchronized Response listAll()
    {

        String entity = JsonRenderers.DEFAULT.render( properties );

        return addHeaders(
                Response.ok( entity, JsonRenderers.DEFAULT.getMediaType() ) ).build();

    }

    @POST
    @Produces( MediaType.APPLICATION_JSON )
    @Consumes( MediaType.APPLICATION_JSON )
    @Path( ALL_SETTINGS_PATH )
    public Response jsonSetMany( String data )
    {
        return setMany( data );
    }

    @POST
    @Produces( MediaType.APPLICATION_JSON )
    @Consumes( MediaType.APPLICATION_FORM_URLENCODED )
    @Path( ALL_SETTINGS_PATH )
    public Response formSetMany( @FormParam( "value" ) String data )
    {
        return setMany( data );
    }

    //
    // INTERNALS
    //

    /**
     * Set one or more settings at once.
     *
     * This method will validate all changes to make, if they pass they will all
     * be applied and appropriate action will be taken to apply changes. This
     * will usually entail restarting the REST server, but can also mean
     * re-spawning the JVM that the whole system runs in.
     *
     * WARNING: Certain settings are only applicable when creating a neo4j
     * database. In the future, changing such a setting will in fact lead to the
     * original database being destroyed and a new one to be created in it's
     * place. The plan is to create a new database and then copy data from the
     * old one to the new, but what the end result will be is not yet decided.
     *
     * @param json is a json array of objects with a "key" and a "value"
     *            property each. Key maps to the setting to change, value should
     *            be the new value to change it to.
     */
    @SuppressWarnings( "unchecked" )
    protected Response setMany( String json )
    {
        try
        {
            json = dodgeStartingUnicodeMarker( json );
            Collection<Object> newProperties = (Collection<Object>) JsonHelper.jsonToSingleValue( json );

            // Validate all properties
            Map<String, Object> currentPropMap;
            ServerPropertyRepresentation currentPropObj;

            boolean hasJvmChanges = false;
            boolean hasCreationChanges = false;
            boolean hasDbConfigChanges = false;

            for ( Object property : newProperties )
            {
                if ( !( property instanceof Map<?, ?> ) )
                {
                    throw new IllegalArgumentException(
                            "'"
                                    + property
                                    + "' is not a valid configuration directive." );
                }

                currentPropMap = (Map<String, Object>) property;
                currentPropObj = properties.get( (String) currentPropMap.get( "key" ) );

                if ( !currentPropObj.isValidValue( (String) currentPropMap.get( "value" ) ) )
                {
                    throw new IllegalArgumentException(
                            "'" + (String) currentPropMap.get( "value" )
                                    + "' is not a valid value for property '"
                                    + (String) currentPropMap.get( "key" )
                                    + "'." );
                }

                // Keep track of what type of changes we are making
                switch ( currentPropObj.getType() )
                {
                case APP_ARGUMENT:
                case JVM_ARGUMENT:
                    hasJvmChanges = true;
                    break;
                case DB_CREATION_PROPERTY:
                    hasCreationChanges = true;
                    break;
                case CONFIG_PROPERTY:
                    hasDbConfigChanges = true;
                    break;
                }
            }

            // Everything is valid, apply properties
            for ( Object property : newProperties )
            {

                currentPropMap = (Map<String, Object>) property;
                properties.set( (String) currentPropMap.get( "key" ),
                        (String) currentPropMap.get( "value" ) );
            }

            // All changes applied, perform required restarts
            if ( hasCreationChanges )
            {
                throw new OperationNotSupportedException();
            }
            else if ( hasJvmChanges )
            {
                // Client has changed settings that require a JVM restart
                DeferredTask.defer( new JvmRestartTask(), 10 );
            }
            else if ( hasDbConfigChanges )
            {
                // Client has changed settings that only require REST-server
                // restart.
                if ( LifecycleService.serverStatus == LifecycleRepresentation.Status.RUNNING )
                {

                    int restPort = WebServerFactory.getDefaultWebServer().getPort();

                    WebServerFactory.getDefaultWebServer().stopServer();
                    DatabaseLocator.shutdownGraphDatabase();
                    WebServerFactory.getDefaultWebServer().startServer(
                            restPort );
                    ConsoleSessions.destroyAllSessions();
                }
            }

            return addHeaders( Response.ok() ).build();
        }
        catch ( PropertyValueException e )
        {
            return buildBadJsonExceptionResponse( json, e,
                    JsonRenderers.DEFAULT );
        }
        catch ( OperationNotSupportedException e )
        {
            return buildExceptionResponse(
                    Status.FORBIDDEN,
                    "Changing settings that required database re-creation is currently not supported.",
                    e, JsonRenderers.DEFAULT );
        }
        catch ( IllegalArgumentException e )
        {
            return buildExceptionResponse( Status.BAD_REQUEST,
                    "You attempted to set an illegal value.", e,
                    JsonRenderers.DEFAULT );
        }
        catch ( NoSuchPropertyException e )
        {
            return buildExceptionResponse( Status.BAD_REQUEST,
                    "You attempted to modify a property that does not exist.",
                    e, JsonRenderers.DEFAULT );
        }
        catch ( IOException e )
        {
            return buildExceptionResponse(
                    Status.INTERNAL_SERVER_ERROR,
                    "Unable to save changes to disk, does daemon user have write permissions?",
                    e, JsonRenderers.DEFAULT );
        }
    }
}
TOP

Related Classes of org.neo4j.webadmin.rest.ConfigService

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.