Package com.dotcms.rest

Source Code of com.dotcms.rest.IntegrityResource

package com.dotcms.rest;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.dotcms.publisher.endpoint.bean.PublishingEndPoint;
import com.dotcms.publisher.endpoint.business.PublishingEndPointAPI;
import com.dotcms.publisher.integrity.IntegrityDataGeneratorThread;
import com.dotcms.publisher.pusher.PushPublisher;
import com.dotcms.publisher.util.TrustFactory;
import com.dotcms.repackage.org.apache.commons.httpclient.HttpStatus;
import com.dotcms.repackage.com.sun.jersey.api.client.Client;
import com.dotcms.repackage.com.sun.jersey.api.client.ClientResponse;
import com.dotcms.repackage.com.sun.jersey.api.client.config.ClientConfig;
import com.dotcms.repackage.com.sun.jersey.api.client.config.DefaultClientConfig;
import com.dotcms.repackage.com.sun.jersey.client.urlconnection.HTTPSProperties;
import com.dotcms.repackage.com.sun.jersey.multipart.FormDataMultiPart;
import com.dotcms.repackage.com.sun.jersey.multipart.FormDataParam;
import com.dotcms.repackage.com.sun.jersey.multipart.file.FileDataBodyPart;
import com.dotcms.repackage.javax.ws.rs.Consumes;
import com.dotcms.repackage.javax.ws.rs.GET;
import com.dotcms.repackage.javax.ws.rs.POST;
import com.dotcms.repackage.javax.ws.rs.Path;
import com.dotcms.repackage.javax.ws.rs.PathParam;
import com.dotcms.repackage.javax.ws.rs.Produces;
import com.dotcms.repackage.javax.ws.rs.WebApplicationException;
import com.dotcms.repackage.javax.ws.rs.core.Context;
import com.dotcms.repackage.javax.ws.rs.core.MediaType;
import com.dotcms.repackage.javax.ws.rs.core.Response;
import com.dotcms.repackage.javax.ws.rs.core.StreamingOutput;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.cms.factories.PublicEncryptionFactory;
import com.dotmarketing.db.HibernateUtil;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotHibernateException;
import com.dotmarketing.util.Config;
import com.dotmarketing.util.ConfigUtils;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.UUIDGenerator;
import com.dotmarketing.util.UtilMethods;
import com.dotmarketing.util.json.JSONArray;
import com.dotmarketing.util.json.JSONException;
import com.dotmarketing.util.json.JSONObject;
import com.liferay.portal.language.LanguageException;
import com.liferay.portal.language.LanguageUtil;
import com.liferay.portal.model.User;


@Path("/integrity")
public class IntegrityResource extends WebResource {

    public enum ProcessStatus {
        PROCESSING, ERROR, FINISHED, NO_CONFLICTS, CANCELED
    }

    public enum IntegrityType {
        FOLDERS("push_publish_integrity_folders_conflicts",
                "FoldersToCheck.csv",
                "FoldersToFix.csv"),

        SCHEMES("push_publish_integrity_schemes_conflicts",
                "SchemesToCheck.csv",
                "SchemesToFix.csv"),

        STRUCTURES("push_publish_integrity_structures_conflicts",
                "StructuresToCheck.csv",
                "StructuresToFix.csv");

        private String label;
        private String dataToCheckCSVName;
        private String dataToFixCSVName;


        IntegrityType(String label,String dataToCheckCSVName,String dataToFixCSVName) {
            this.label = label;
            this.dataToCheckCSVName = dataToCheckCSVName;
            this.dataToFixCSVName = dataToFixCSVName;
        }

        public String getLabel() {
            return label;
        }

        public String getDataToCheckCSVName() {
            return dataToCheckCSVName;
        }

        public String getDataToFixCSVName() {
            return dataToFixCSVName;
        }

    }

    public static final String INTEGRITY_DATA_TO_CHECK_ZIP_FILE_NAME = "DataToCheck.zip";
    public static final String INTEGRITY_DATA_TO_FIX_ZIP_FILE_NAME = "DataToFix.zip";

    /**
     * <p>Returns a zip with data from structures, workflow schemes and folders for integrity check
     *
     * Usage: /getdata
     *
     */

    @POST
    @Path("/generateintegritydata/{params:.*}")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces("text/plain")
    public Response generateIntegrityData(@Context HttpServletRequest request, @FormDataParam("AUTH_TOKEN") String auth_token_enc)  {

        String remoteIP = null;
        try {

            if ( !UtilMethods.isSet( auth_token_enc ) ) {
                return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( "Error: 'endpoint' is a required param." ).build();
            }


            String auth_token = PublicEncryptionFactory.decryptString(auth_token_enc);
            remoteIP = request.getRemoteHost();
            if(!UtilMethods.isSet(remoteIP))
                remoteIP = request.getRemoteAddr();

            PublishingEndPointAPI endpointAPI = APILocator.getPublisherEndPointAPI();
            final PublishingEndPoint requesterEndPoint = endpointAPI.findEnabledSendingEndPointByAddress(remoteIP);

            if(!BundlePublisherResource.isValidToken(auth_token, remoteIP, requesterEndPoint)) {
                return Response.status(HttpStatus.SC_UNAUTHORIZED).build();
            }

            ServletContext servletContext = request.getSession().getServletContext();

            if(servletContext.getAttribute("integrityRunning")!=null && ((Boolean) servletContext.getAttribute("integrityRunning"))) {
                throw new WebApplicationException(Response.status(HttpStatus.SC_CONFLICT).entity("Already Running").build());
            }

            String transactionId = UUIDGenerator.generateUuid();
            servletContext.setAttribute("integrityDataRequestID", transactionId);

            // start data generation process
            IntegrityDataGeneratorThread idg = new IntegrityDataGeneratorThread( requesterEndPoint, request.getSession().getServletContext() );
            idg.start();
            //Saving the thread on the session context for a later use
            servletContext.setAttribute( "integrityDataGeneratorThread_" + transactionId, idg );

            return Response.ok(transactionId).build();

        } catch (Exception e) {
            Logger.error(IntegrityResource.class, "Error caused by remote call of: "+remoteIP);
            Logger.error(IntegrityResource.class,e.getMessage(),e);
        }


        return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).build();

    }

    /**
     * Checks if the generation of Integrity Data is done.
     * If FINISHED, returns a zip with the data
     * if PROCESSING, returns HttpStatus.SC_PROCESSING
     * if ERROR, returns HttpStatus.SC_INTERNAL_SERVER_ERROR, including the error message
     *
     * Usage: /getdata
     *
     */
    @POST
    @Path("/getintegritydata/{params:.*}")
    @Produces("application/zip")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    public Response getIntegrityData(@Context HttpServletRequest request, @FormDataParam("AUTH_TOKEN") String auth_token_enc, @FormDataParam("REQUEST_ID") String requestId)  {
        String remoteIP = null;

        try {

            String auth_token = PublicEncryptionFactory.decryptString(auth_token_enc);
            remoteIP = request.getRemoteHost();
            if(!UtilMethods.isSet(remoteIP))
                remoteIP = request.getRemoteAddr();

            PublishingEndPointAPI endpointAPI = APILocator.getPublisherEndPointAPI();
            final PublishingEndPoint requesterEndPoint = endpointAPI.findEnabledSendingEndPointByAddress(remoteIP);

            if(!BundlePublisherResource.isValidToken(auth_token, remoteIP, requesterEndPoint) || !UtilMethods.isSet(requestId)) {
                return Response.status(HttpStatus.SC_UNAUTHORIZED).build();
            }

            ServletContext servletContext = request.getSession().getServletContext();
            if(!UtilMethods.isSet(servletContext.getAttribute("integrityDataRequestID"))
                    || !((String) servletContext.getAttribute("integrityDataRequestID")).equals(requestId)) {
                return Response.status(HttpStatus.SC_UNAUTHORIZED).build();
            }

            ProcessStatus integrityDataGeneratorStatus = (ProcessStatus) servletContext.getAttribute("integrityDataGenerationStatus");

            if(UtilMethods.isSet( integrityDataGeneratorStatus )) {
                switch (integrityDataGeneratorStatus) {
                    case PROCESSING:
                        return Response.status(HttpStatus.SC_PROCESSING).build();
                    case FINISHED:
                        StreamingOutput output = new StreamingOutput() {
                            public void write(OutputStream output) throws IOException, WebApplicationException {
                                InputStream is = new FileInputStream(ConfigUtils.getIntegrityPath() + File.separator + requesterEndPoint.getId() + File.separator + INTEGRITY_DATA_TO_CHECK_ZIP_FILE_NAME);

                                byte[] buffer = new byte[1024];
                                int bytesRead;
                                //read from is to buffer
                                while((bytesRead = is.read(buffer)) !=-1){
                                    output.write(buffer, 0, bytesRead);
                                }
                                is.close();
                                //flush OutputStream to write any buffered data to file
                                output.flush();
                                output.close();

                            }
                        };
                        return Response.ok(output).build();

                    case CANCELED:
                        return Response.status( HttpStatus.SC_RESET_CONTENT ).entity( servletContext.getAttribute( "integrityDataGenerationError" ) ).build();

                    case ERROR:
                        return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(servletContext.getAttribute("integrityDataGenerationError")).build();

                    default:
                        break;
                }
            }

        } catch (Exception e) {
            Logger.error(IntegrityResource.class, "Error caused by remote call of: "+remoteIP, e);
            return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
        }

        return Response.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).build();

    }

    @GET
    @Path("/checkintegrity/{params:.*}")
    @Produces (MediaType.APPLICATION_JSON)
    public Response checkIntegrity(@Context HttpServletRequest request, @PathParam("params") String params)  {
        InitDataObject initData = init(params, true, request, true);

        Map<String, String> paramsMap = initData.getParamsMap();

        final HttpSession session = request.getSession();
        final User loggedUser = initData.getUser();

        JSONObject jsonResponse = new JSONObject();

        //Validate the parameters
        final String endpointId = paramsMap.get( "endpoint" );
        if ( !UtilMethods.isSet( endpointId ) ) {
            return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( "Error: endpoint is a required Field.").build();
        }


        // return if we already have the data
        try {
            IntegrityUtil integrityUtil = new IntegrityUtil();

            if(integrityUtil.doesIntegrityConflictsDataExist(endpointId)) {

                jsonResponse.put( "success", true );
                jsonResponse.put( "message", "Integrity Checking Initialized..." );

                //Setting the process status
                setStatus( request, endpointId, ProcessStatus.FINISHED );

                return response( jsonResponse.toString(), false );
            }
        } catch(JSONException e) {
            Logger.error(IntegrityResource.class, "Error setting return message in JSON response", e);
            return response( "Error setting return message in JSON response" , true );
        } catch(Exception e) {
            Logger.error(IntegrityResource.class, "Error checking existence of integrity data", e);
            return response( "Error checking existence of integrity data" , true );
        }

        try {

            //Setting the process status
            setStatus( request, endpointId, ProcessStatus.PROCESSING );

            final Client client = getRESTClient();

            final PublishingEndPoint endpoint = APILocator.getPublisherEndPointAPI().findEndPointById(endpointId);
            final String authToken = PushPublisher.retriveKeyString(PublicEncryptionFactory.decryptString(endpoint.getAuthKey().toString()));

            FormDataMultiPart form = new FormDataMultiPart();
            form.field("AUTH_TOKEN",authToken);

            //Sending bundle to endpoint
            String url = endpoint.toURL()+"/api/integrity/generateintegritydata/";
            com.dotcms.repackage.com.sun.jersey.api.client.WebResource resource = client.resource(url);

            ClientResponse response =
                    resource.type(MediaType.MULTIPART_FORM_DATA).post(ClientResponse.class, form);

            if(response.getClientResponseStatus().getStatusCode() == HttpStatus.SC_OK) {
                final String integrityDataRequestID = response.getEntity(String.class);

                Thread integrityDataRequestChecker = new Thread() {
                    public void run(){

                        FormDataMultiPart form = new FormDataMultiPart();
                        form.field("AUTH_TOKEN",authToken);
                        form.field("REQUEST_ID",integrityDataRequestID);

                        String url = endpoint.toURL()+"/api/integrity/getintegritydata/";
                        com.dotcms.repackage.com.sun.jersey.api.client.WebResource resource = client.resource(url);

                        boolean processing = true;

                        while(processing) {

                            ClientResponse response = resource.type( MediaType.MULTIPART_FORM_DATA ).post( ClientResponse.class, form );
                            if ( response.getClientResponseStatus() != null && response.getClientResponseStatus().getStatusCode() == HttpStatus.SC_OK ) {

                                processing = false;

                                InputStream zipFile = response.getEntityInputStream();
                                String outputDir = ConfigUtils.getIntegrityPath() + File.separator + endpoint.getId();

                                try {

                                    IntegrityUtil.unzipFile(zipFile, outputDir);

                                } catch(Exception e) {

                                    //Special handling if the thread was interrupted
                                    if ( e instanceof InterruptedException ) {
                                        //Setting the process status
                                        setStatus( session, endpointId, ProcessStatus.CANCELED, null );
                                        Logger.debug( IntegrityResource.class, "Requested interruption of the integrity checking process [unzipping Integrity Data] by the user.", e );
                                        throw new RuntimeException( "Requested interruption of the integrity checking process [unzipping Integrity Data] by the user.", e );
                                    }

                                    //Setting the process status
                                    setStatus( session, endpointId, ProcessStatus.ERROR, null );
                                    Logger.error(IntegrityResource.class, "Error while unzipping Integrity Data", e);
                                    throw new RuntimeException("Error while unzipping Integrity Data", e);
                                }

                                // set session variable
                                // call IntegrityChecker

                                Boolean foldersConflicts = false;
                                Boolean structuresConflicts = false;
                                Boolean schemesConflicts = false;

                                IntegrityUtil integrityUtil = new IntegrityUtil();
                                try {
                                    HibernateUtil.startTransaction();

                                    foldersConflicts = integrityUtil.checkFoldersIntegrity(endpointId);
                                    structuresConflicts = integrityUtil.checkStructuresIntegrity(endpointId);
                                    schemesConflicts = integrityUtil.checkWorkflowSchemesIntegrity(endpointId);

                                    HibernateUtil.commitTransaction();


                                } catch(Exception e) {

                                    try {
                                        HibernateUtil.rollbackTransaction();
                                    } catch (DotHibernateException e1) {
                                        Logger.error(IntegrityResource.class, "Error while rolling back transaction", e);
                                    }

                                    //Special handling if the thread was interrupted
                                    if ( e instanceof InterruptedException ) {
                                        //Setting the process status
                                        setStatus( session, endpointId, ProcessStatus.CANCELED, null );
                                        Logger.debug( IntegrityResource.class, "Requested interruption of the integrity checking process by the user.", e );
                                        throw new RuntimeException( "Requested interruption of the integrity checking process by the user.", e );
                                    }

                                    Logger.error(IntegrityResource.class, "Error checking integrity", e);

                                    //Setting the process status
                                    setStatus( session, endpointId, ProcessStatus.ERROR, null );
                                    throw new RuntimeException("Error checking integrity", e);
                                } finally {
                                    try {
                                        integrityUtil.dropTempTables(endpointId);
                                    } catch (DotDataException e) {
                                        Logger.error(IntegrityResource.class, "Error while deleting temp tables", e);
                                    }
                                }

                                if ( !foldersConflicts && !structuresConflicts && !schemesConflicts ) {
                                    String noConflictMessage;
                                    try {
                                        noConflictMessage = LanguageUtil.get( loggedUser.getLocale(), "push_publish_integrity_conflicts_not_found" );
                                    } catch ( LanguageException e ) {
                                        noConflictMessage = "No Integrity Conflicts found";
                                    }
                                    //Setting the process status
                                    setStatus( session, endpointId, ProcessStatus.NO_CONFLICTS, noConflictMessage );
                                } else {
                                    //Setting the process status
                                    setStatus( session, endpointId, ProcessStatus.FINISHED, null );
                                }

                            } else if ( response.getClientResponseStatus() == null && response.getStatus() == HttpStatus.SC_PROCESSING ) {
                                continue;
                            } else if ( response.getClientResponseStatus() == null && response.getStatus() == HttpStatus.SC_RESET_CONTENT ) {
                                processing = false;
                                //Setting the process status
                                setStatus( session, endpointId, ProcessStatus.CANCELED, null );
                            } else {
                                setStatus( session, endpointId, ProcessStatus.ERROR, null );
                                Logger.error( this.getClass(), "Response indicating a " + response.getClientResponseStatus().getReasonPhrase() + " (" + response.getClientResponseStatus().getStatusCode() + ") Error trying to retrieve the Integrity data from the Endpoint [" + endpointId + "]." );
                                processing = false;
                            }
                        }
                    }
                };

                //Start the integrity check
                integrityDataRequestChecker.start();
                addThreadToSession( session, integrityDataRequestChecker, endpointId, integrityDataRequestID );

            } else if ( response.getClientResponseStatus().getStatusCode() == HttpStatus.SC_UNAUTHORIZED ) {
                setStatus( session, endpointId, ProcessStatus.ERROR, null );
                Logger.error( this.getClass(), "Response indicating Not Authorized received from Endpoint. Please check Auth Token. Endpoint Id: " + endpointId );
                return response( "Response indicating Not Authorized received from Endpoint. Please check Auth Token. Endpoint Id:" + endpointId, true );
            } else {
                setStatus( session, endpointId, ProcessStatus.ERROR, null );
                Logger.error( this.getClass(), "Response indicating a " + response.getClientResponseStatus().getReasonPhrase() + " (" + response.getClientResponseStatus().getStatusCode() + ") Error trying to connect with the Integrity API on the Endpoint. Endpoint Id: " + endpointId );
                return response( "Response indicating a " + response.getClientResponseStatus().getReasonPhrase() + " (" + response.getClientResponseStatus().getStatusCode() + ") Error trying to connect with the Integrity API on the Endpoint. Endpoint Id: " + endpointId, true );
            }

            jsonResponse.put( "success", true );
            jsonResponse.put( "message", "Integrity Checking Initialized..." );

        } catch(Exception e) {

            //Special handling if the thread was interrupted
            if ( e instanceof InterruptedException || e.getCause() instanceof InterruptedException ) {
                //Setting the process status
                setStatus( session, endpointId, ProcessStatus.CANCELED, null );
                Logger.debug( IntegrityResource.class, "Requested interruption of the integrity checking process by the user.", e );
                return response( "Requested interruption of the integrity checking process by the user for End Point server: [" + endpointId + "]" , true );
            }

            //Setting the process status
            setStatus( session, endpointId, ProcessStatus.ERROR, null );
            Logger.error( this.getClass(), "Error initializing the integrity checking process for End Point server: [" + endpointId + "]", e );
            return response( "Error initializing the integrity checking process for End Point server: [" + endpointId + "]" , true );
        }


        return response( jsonResponse.toString(), false );

    }

    /**
     * Method that will interrupt the integrity checking running processes locally and in the end point server
     *
     * @param request
     * @param params
     * @return
     * @throws JSONException
     */
    @GET
    @Path ("/cancelIntegrityProcess/{params:.*}")
    @Produces (MediaType.APPLICATION_JSON)
    public Response cancelIntegrityProcess ( @Context HttpServletRequest request, @PathParam ("params") String params ) throws JSONException {

        StringBuilder responseMessage = new StringBuilder();

        InitDataObject initData = init( params, true, request, true );
        Map<String, String> paramsMap = initData.getParamsMap();

        //Validate the parameters
        String endpointId = paramsMap.get( "endpoint" );
        if ( !UtilMethods.isSet( endpointId ) ) {
            Response.ResponseBuilder responseBuilder = Response.status( HttpStatus.SC_BAD_REQUEST );
            responseBuilder.entity( responseMessage.append( "Error: " ).append( "endpoint" ).append( " is a required Field." ) );

            return responseBuilder.build();
        }

        try {
            JSONObject jsonResponse = new JSONObject();

            HttpSession session = request.getSession();
            //Verify if we have something set on the session
            if ( session.getAttribute( "integrityCheck_" + endpointId ) == null ) {
                //And prepare the response
                jsonResponse.put( "success", false );
                jsonResponse.put( "message", "No checking process found for End point server [" + endpointId + "]" );
            } else if ( session.getAttribute( "integrityThread_" + endpointId ) == null ) {
                //And prepare the response
                jsonResponse.put( "success", false );
                jsonResponse.put( "message", "No checking process found for End point server [" + endpointId + "]" );
            } else {

                //Search for the status on session
                ProcessStatus status = (ProcessStatus) session.getAttribute( "integrityCheck_" + endpointId );

                //And prepare the response
                jsonResponse.put( "endPoint", endpointId );
                if ( status == ProcessStatus.PROCESSING ) {

                    //Get the thread associated to this endpoint and the integrity request id
                    Thread runningThread = (Thread) session.getAttribute( "integrityThread_" + endpointId );
                    String integrityDataRequestId = (String) session.getAttribute( "integrityDataRequest_" + endpointId );

                    //Find the registered auth token in order to connect to the end point server
                    PublishingEndPoint endpoint = APILocator.getPublisherEndPointAPI().findEndPointById( endpointId );
                    String authToken = PushPublisher.retriveKeyString( PublicEncryptionFactory.decryptString( endpoint.getAuthKey().toString() ) );

                    FormDataMultiPart form = new FormDataMultiPart();
                    form.field( "AUTH_TOKEN", authToken );
                    form.field( "REQUEST_ID", integrityDataRequestId );

                    //Prepare the connection
                    Client client = getRESTClient();
                    String url = endpoint.toURL() + "/api/integrity/cancelIntegrityProcessOnEndpoint/";
                    com.dotcms.repackage.com.sun.jersey.api.client.WebResource resource = client.resource( url );

                    //Execute the call
                    ClientResponse response = resource.type( MediaType.MULTIPART_FORM_DATA ).post( ClientResponse.class, form );

                    if ( response.getClientResponseStatus().getStatusCode() == HttpStatus.SC_OK ) {
                        //Nothing to do here, we found no process to cancel
                    } else if ( response.getClientResponseStatus().getStatusCode() == HttpStatus.SC_RESET_CONTENT ) {
                        //Expected return status if a cancel was made on the end point server
                    } else {
                        Logger.error( this.getClass(), "Response indicating a " + response.getClientResponseStatus().getReasonPhrase() + " (" + response.getClientResponseStatus().getStatusCode() + ") Error trying to interrupt the running process on the Endpoint [ " + endpointId + "]." );
                    }

                    //Interrupt the Thread process
                    runningThread.interrupt();

                    //Remove the thread from the session
                    clearThreadInSession( request, endpointId );

                    jsonResponse.put( "success", true );
                    jsonResponse.put( "message", LanguageUtil.get( initData.getUser().getLocale(), "IntegrityCheckingCanceled" ) );
                } else {
                    jsonResponse.put( "success", false );
                    jsonResponse.put( "message", "The integrity process for End Point server: [" + endpointId + "] was already stopped." );
                }
            }

            responseMessage.append( jsonResponse.toString() );

        } catch ( Exception e ) {
            Logger.error( this.getClass(), "Error checking the integrity process status for End Point server: [" + endpointId + "]", e );
            return response( "Error checking the integrity process status for End Point server: [" + endpointId + "]", true );
        }

        return response( responseMessage.toString(), false );
    }

    /**
     * Method expected to run on an end point server in order to interrupt the integrity checking process if running
     *
     * @param request
     * @param auth_token_enc
     * @param requestId
     * @return
     */
    @POST
    @Path("/cancelIntegrityProcessOnEndpoint/{params:.*}")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces (MediaType.APPLICATION_JSON)
    public Response cancelIntegrityProcessOnEndpoint ( @Context HttpServletRequest request, @FormDataParam ("AUTH_TOKEN") String auth_token_enc, @FormDataParam ("REQUEST_ID") String requestId ) {

        String remoteIP = null;

        try {

            remoteIP = request.getRemoteHost();
            if ( !UtilMethods.isSet( remoteIP ) ) {
                remoteIP = request.getRemoteAddr();
            }

            //Search for the given end point
            PublishingEndPointAPI endpointAPI = APILocator.getPublisherEndPointAPI();
            PublishingEndPoint requesterEndPoint = endpointAPI.findEnabledSendingEndPointByAddress( remoteIP );

            //Verify the authentication token
            String auth_token = PublicEncryptionFactory.decryptString( auth_token_enc );
            if ( !BundlePublisherResource.isValidToken( auth_token, remoteIP, requesterEndPoint ) || !UtilMethods.isSet( requestId ) ) {
                return Response.status( HttpStatus.SC_UNAUTHORIZED ).build();
            }

            ServletContext servletContext = request.getSession().getServletContext();
            if ( !UtilMethods.isSet( servletContext.getAttribute( "integrityDataRequestID" ) ) || !((String) servletContext.getAttribute( "integrityDataRequestID" )).equals( requestId ) ) {
                return Response.status( HttpStatus.SC_UNAUTHORIZED ).build();
            }

            //Verify the status and if the process it is still running we will interrupt it
            ProcessStatus integrityDataGeneratorStatus = (ProcessStatus) servletContext.getAttribute( "integrityDataGenerationStatus" );
            if ( UtilMethods.isSet( integrityDataGeneratorStatus ) ) {
                switch ( integrityDataGeneratorStatus ) {
                    case PROCESSING:

                        //Verify if the thread is on the session for this given request id
                        if ( servletContext.getAttribute( "integrityDataGeneratorThread_" + requestId ) != null ) {

                            //If found interrupt the process
                            IntegrityDataGeneratorThread integrityDataGeneratorThread = (IntegrityDataGeneratorThread) servletContext.getAttribute( "integrityDataGeneratorThread_" + requestId );
                            integrityDataGeneratorThread.interrupt();
                            servletContext.removeAttribute( "integrityDataGeneratorThread_" + requestId );

                            return Response.status( HttpStatus.SC_RESET_CONTENT ).entity( "Interrupted checking process on End Point server ( " + remoteIP + ")." ).build();
                        }
                    default:
                        break;
                }
            }

        } catch ( Exception e ) {
            Logger.error( IntegrityResource.class, "Error caused by remote call of: " + remoteIP, e );
            return response( "Error interrupting checking process on End Point server ( " + remoteIP + "). [" + e.getMessage() + "]", true );
        }

        return response( "Interrupted checking process on End Point server ( " + remoteIP + ").", false );
    }

    /**
     * Method that will verify the status of a check integrity process for a given server
     *
     * @param request
     * @param params
     * @return
     * @throws JSONException
     */
    @GET
    @Path ("/checkIntegrityProcessStatus/{params:.*}")
    @Produces (MediaType.APPLICATION_JSON)
    public Response checkIntegrityProcessStatus ( @Context final HttpServletRequest request, @PathParam ("params") String params ) throws JSONException {

        StringBuilder responseMessage = new StringBuilder();

        InitDataObject initData = init( params, true, request, true );
        Map<String, String> paramsMap = initData.getParamsMap();

        //Validate the parameters
        String endpointId = paramsMap.get( "endpoint" );
        if ( !UtilMethods.isSet( endpointId ) ) {
            Response.ResponseBuilder responseBuilder = Response.status( HttpStatus.SC_BAD_REQUEST );
            responseBuilder.entity( responseMessage.append( "Error: " ).append( "endpoint" ).append( " is a required Field." ) );

            return responseBuilder.build();
        }

        try {
            JSONObject jsonResponse = new JSONObject();

            HttpSession session = request.getSession();
            //Verify if we have something set on the session
            if ( session.getAttribute( "integrityCheck_" + endpointId ) == null ) {
                //And prepare the response
                jsonResponse.put( "success", true );
                jsonResponse.put( "message", "No checking process found for End point server [" + endpointId + "]" );
                jsonResponse.put( "status", "nopresent" );
            } else {

                //Search for the status on session
                ProcessStatus status = (ProcessStatus) session.getAttribute( "integrityCheck_" + endpointId );

                //And prepare the response
                jsonResponse.put( "success", true );
                jsonResponse.put( "endPoint", endpointId );
                if ( status == ProcessStatus.PROCESSING ) {
                    jsonResponse.put( "status", "processing" );
                    jsonResponse.put( "message", "Success" );
                } else if ( status == ProcessStatus.FINISHED ) {
                    jsonResponse.put( "status", "finished" );
                    jsonResponse.put( "message", "Success" );
                } else if ( status == ProcessStatus.NO_CONFLICTS ) {
                    jsonResponse.put( "status", "noConflicts" );
                    jsonResponse.put( "message", session.getAttribute( "integrityCheck_message_" + endpointId ) );
                    clearStatus( request, endpointId );
                } else if ( status == ProcessStatus.CANCELED) {
                    jsonResponse.put( "status", "canceled" );
                    jsonResponse.put( "message", LanguageUtil.get( initData.getUser().getLocale(), "IntegrityCheckingCanceled" ) );
                    clearStatus( request, endpointId );
                } else {
                    jsonResponse.put( "status", "error" );
                    jsonResponse.put( "message", "Error checking the integrity process status for End Point server: [" + endpointId + "]" );
                    clearStatus( request, endpointId );
                }
            }

            responseMessage.append( jsonResponse.toString() );

        } catch ( Exception e ) {
            Logger.error( this.getClass(), "Error checking the integrity process status for End Point server: [" + endpointId + "]", e );
            return response( "Error checking the integrity process status for End Point server: [" + endpointId + "]" , true );
        }

        return response( responseMessage.toString(), false );
    }

    /**
     * Generates and returns the integrity check results for a given server
     *
     * @param request
     * @param params
     * @return
     * @throws JSONException
     */
    @GET
    @Path ("/getIntegrityResult/{params:.*}")
    @Produces (MediaType.APPLICATION_JSON)
    public Response getIntegrityResult ( @Context HttpServletRequest request, @PathParam ("params") String params ) throws JSONException {

        StringBuilder responseMessage = new StringBuilder();

        InitDataObject initData = init( params, true, request, true );
        Map<String, String> paramsMap = initData.getParamsMap();

        //Validate the parameters
        String endpointId = paramsMap.get( "endpoint" );
        if ( !UtilMethods.isSet( endpointId ) ) {
            Response.ResponseBuilder responseBuilder = Response.status( HttpStatus.SC_BAD_REQUEST );
            responseBuilder.entity( responseMessage.append( "Error: " ).append( "endpoint" ).append( " is a required Field." ) );

            return responseBuilder.build();
        }

        try {

            JSONObject jsonResponse = new JSONObject();
            IntegrityUtil integrityUtil = new IntegrityUtil();

            //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            //Structures tab data
            JSONArray tabResponse = null;
            JSONObject errorContent = null;

            IntegrityType[] types = IntegrityType.values();
            boolean isThereAnyConflict = false;

            for (IntegrityType integrityType : types) {
                tabResponse = new JSONArray();
                errorContent = new JSONObject();

                errorContent.put( "title",   LanguageUtil.get( initData.getUser().getLocale(), integrityType.getLabel() )  );//Title of the check

                List<Map<String, Object>> results = integrityUtil.getIntegrityConflicts(endpointId, integrityType);

                JSONArray columns = new JSONArray();

                switch (integrityType) {
                    case STRUCTURES:
                        columns.add("velocity_name");
                        break;
                    case FOLDERS:
                        columns.add("folder");
                        break;
                    case SCHEMES:
                        columns.add("name");
                        break;
                }

                columns.add("local_inode");
                columns.add("remote_inode");

                errorContent.put( "columns", columns.toArray() );

                if(!results.isEmpty()) {
                    // the columns names are the keys in the results
                    isThereAnyConflict = isThereAnyConflict || true;

                    JSONArray values = new JSONArray();
                    for (Map<String, Object> result : results) {

                        JSONObject columnsContent = new JSONObject();

                        for (String keyName : result.keySet()) {
                            columnsContent.put(keyName, result.get(keyName));
                        }

                        values.put(columnsContent);
                    }

                    errorContent.put( "values", values.toArray() );
                } else {
                    errorContent.put( "values", new JSONArray().toArray() );
                }

                tabResponse.add( errorContent );
                //And prepare the response
                jsonResponse.put( integrityType.name().toLowerCase(), tabResponse.toArray() );
            }

            if(!isThereAnyConflict) {
                clearStatus( request, endpointId );
            }

            /*
            ++++++++++++++++++++++++
            Important just in case of return custom errors
             */
            jsonResponse.put( "success", true );
            jsonResponse.put( "message", "Success" );

            responseMessage.append( jsonResponse.toString() );
        } catch ( Exception e ) {
            Logger.error( this.getClass(), "Error generating the integrity result for End Point server: [" + endpointId + "]", e );
            return response( "Error generating the integrity result for End Point server: [" + endpointId + "]" , true );
        }

        return response( responseMessage.toString(), false );
    }

    /**
     * Method that will discard the conflicts between local node and given endpoint
     *
     * @param request
     * @param params
     * @return
     * @throws JSONException
     */
    @GET
    @Path ("/discardconflicts/{params:.*}")
    @Produces (MediaType.APPLICATION_JSON)
    public Response discardConflicts ( @Context final HttpServletRequest request, @PathParam ("params") String params ) throws JSONException {

        StringBuilder responseMessage = new StringBuilder();

        InitDataObject initData = init( params, true, request, true );
        Map<String, String> paramsMap = initData.getParamsMap();

        //Validate the parameters
        String endpointId = paramsMap.get( "endpoint" );
        String type = paramsMap.get( "type" );

        if ( !UtilMethods.isSet( endpointId ) ) {
            return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( responseMessage.append( "Error: " ).append( "'endpoint'" ).append( " is a required param." )).build();
        }

        if ( !UtilMethods.isSet( type ) ) {
            return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( responseMessage.append( "Error: " ).append( "'type'" ).append( " is a required param." )).build();
        }

        try {
            JSONObject jsonResponse = new JSONObject();

            IntegrityUtil integrityUtil = new IntegrityUtil();
            integrityUtil.discardConflicts(endpointId, IntegrityType.valueOf(type.toUpperCase()));

            clearStatus( request, endpointId );

            responseMessage.append( jsonResponse.toString() );

        } catch ( Exception e ) {
            Logger.error( this.getClass(), "Error discarding "+type+" conflicts for End Point server: [" + endpointId + "]", e );
            return response( "Error discarding "+type+" conflicts for End Point server: [" + endpointId + "]" , true );
        }

        return response( responseMessage.toString(), false );
    }

    /**
     * Method that will fix the conflicts received from remote
     *
     * @param request
     * @param dataToFix
     * @param auth_token_enc
     * @param type
     * @return
     * @throws JSONException
     */
    @POST
    @Path("/fixconflictsfromremote/{params:.*}")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces("text/plain")
    public Response fixConflictsFromRemote ( @Context final HttpServletRequest request,
                                             @FormDataParam("DATA_TO_FIX") InputStream dataToFix, @FormDataParam("AUTH_TOKEN") String auth_token_enc,
                                             @FormDataParam("TYPE") String type ) throws JSONException {

        String remoteIP = null;
        JSONObject jsonResponse = new JSONObject();

        try {
            String auth_token = PublicEncryptionFactory.decryptString(auth_token_enc);
            remoteIP = request.getRemoteHost();
            if(!UtilMethods.isSet(remoteIP))
                remoteIP = request.getRemoteAddr();

            PublishingEndPointAPI endpointAPI = APILocator.getPublisherEndPointAPI();
            final PublishingEndPoint requesterEndPoint = endpointAPI.findEnabledSendingEndPointByAddress(remoteIP);

            if(!BundlePublisherResource.isValidToken(auth_token, remoteIP, requesterEndPoint)) {
                return Response.status(HttpStatus.SC_UNAUTHORIZED).build();
            }

            IntegrityUtil integrityUtil = new IntegrityUtil();
            HibernateUtil.startTransaction();
            integrityUtil.fixConflicts(dataToFix, requesterEndPoint.getId(), IntegrityType.valueOf(type.toUpperCase()) );
            HibernateUtil.commitTransaction();


        } catch ( Exception e ) {
            try {
                HibernateUtil.rollbackTransaction();
            } catch (DotHibernateException e1) {
                Logger.error(IntegrityResource.class, "Error while rolling back transaction", e);
            }
            Logger.error( this.getClass(), "Error fixing "+type+" conflicts from remote", e );
            return response( "Error fixing "+type+" conflicts from remote" , true );
        }

        jsonResponse.put( "success", true );
        jsonResponse.put( "message", "Conflicts fixed in Remote Endpoint" );
        return response( jsonResponse.toString(), false );



    }
    /**
     * Method that will fix the conflicts between local and remote.
     * If param 'whereToFix' == local, the fix will take place in local node
     * If param 'whereToFix' == remote, the fix will take place in remote node
     *
     * @param request
     * @param params
     * @return
     * @throws JSONException
     */
    @GET
    @Path ("/fixconflicts/{params:.*}")
    @Produces (MediaType.APPLICATION_JSON)
    public Response fixConflicts ( @Context final HttpServletRequest request, @PathParam ("params") String params ) throws JSONException {

        InitDataObject initData = init( params, true, request, true );
        Map<String, String> paramsMap = initData.getParamsMap();
        JSONObject jsonResponse = new JSONObject();

        //Validate the parameters
        String endpointId = paramsMap.get( "endpoint" );
        String type = paramsMap.get( "type" );
        String whereToFix = paramsMap.get( "wheretofix" );

        if ( !UtilMethods.isSet( endpointId ) ) {
            return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( "Error: 'endpoint' is a required param." ).build();
        }

        if ( !UtilMethods.isSet( type ) ) {
            return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( "Error: 'type' is a required param." ).build();
        }

        if ( !UtilMethods.isSet( whereToFix ) ) {
            return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( "Error: 'whereToFix' is a required param." ).build();
        }



        try {

            IntegrityUtil integrityUtil = new IntegrityUtil();

            if(whereToFix.equals("local")) {

              HibernateUtil.startTransaction();
                integrityUtil.fixConflicts(endpointId, IntegrityType.valueOf(type.toUpperCase()));
                HibernateUtil.commitTransaction();
                jsonResponse.put( "success", true );
                jsonResponse.put( "message", "Conflicts fixed in Local Endpoint" );

                clearStatus( request, endpointId );

            } else  if(whereToFix.equals("remote")) {
                integrityUtil.generateDataToFixZip(endpointId, IntegrityType.valueOf(type.toUpperCase()));

                final Client client = getRESTClient();

                PublishingEndPoint endpoint = APILocator.getPublisherEndPointAPI().findEndPointById(endpointId);
                String outputPath = ConfigUtils.getIntegrityPath() + File.separator + endpointId;
                File bundle = new File(outputPath + File.separator + INTEGRITY_DATA_TO_FIX_ZIP_FILE_NAME);

                FormDataMultiPart form = new FormDataMultiPart();
                form.field("AUTH_TOKEN",
                        PushPublisher.retriveKeyString(
                                PublicEncryptionFactory.decryptString(endpoint.getAuthKey().toString())));

                form.field("TYPE", type);
                form.bodyPart(new FileDataBodyPart("DATA_TO_FIX", bundle, MediaType.MULTIPART_FORM_DATA_TYPE));

                String url = endpoint.toURL()+"/api/integrity/fixconflictsfromremote/";
                com.dotcms.repackage.com.sun.jersey.api.client.WebResource resource = client.resource(url);

                ClientResponse response = resource.type(MediaType.MULTIPART_FORM_DATA).post(ClientResponse.class, form);

                if(response.getClientResponseStatus().getStatusCode() == HttpStatus.SC_OK) {
                    jsonResponse.put( "success", true );
                    jsonResponse.put( "message", "Fix Conflicts Process successfully started at Remote." );

                    integrityUtil.discardConflicts(endpointId, IntegrityType.valueOf(type.toUpperCase()));

                    clearStatus( request, endpointId );


                } else {
                    return Response.status( HttpStatus.SC_BAD_REQUEST ).entity("Endpoint with id: " + endpointId + " returned server error." ).build();
                }
            } else {
                return Response.status( HttpStatus.SC_BAD_REQUEST ).entity( "Error: 'whereToFix' has an invalid value.").build();
            }

        } catch ( Exception e ) {
          try {
                HibernateUtil.rollbackTransaction();
            } catch (DotHibernateException e1) {
                Logger.error(IntegrityResource.class, "Error while rolling back transaction", e);
            }

            Logger.error( this.getClass(), "Error fixing "+type+" conflicts for End Point server: [" + endpointId + "]", e );
            return response( "Error fixing conflicts for endpoint: " + endpointId , true );
        }

        return response( jsonResponse.toString(), false );
    }

    private Client getRESTClient() {
        TrustFactory tFactory = new TrustFactory();
        ClientConfig cc = new DefaultClientConfig();

        if(Config.getStringProperty("TRUSTSTORE_PATH") != null && !Config.getStringProperty("TRUSTSTORE_PATH").trim().equals("")) {
            cc.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(tFactory.getHostnameVerifier(), tFactory.getSSLContext()));
        }
        final Client client = Client.create(cc);
        return client;
    }

    /**
     * Removes the status for the checking integrity process of a given enpoint from session
     *
     * @param request
     * @param endpointId
     */
    private void clearStatus ( HttpServletRequest request, String endpointId ) {
        clearStatus( request.getSession(), endpointId );
    }

    /**
     * Removes the status for the checking integrity process of a given enpoint from session
     *
     * @param session
     * @param endpointId
     */
    private void clearStatus ( HttpSession session, String endpointId ) {
        session.removeAttribute( "integrityCheck_" + endpointId );
        session.removeAttribute( "integrityCheck_message_" + endpointId );
        clearThreadInSession( session, endpointId );
    }

    /**
     * Sets the status for the checking integrity process of a given enpoint in session
     *
     * @param request
     * @param endpointId
     * @param status
     */
    private void setStatus ( HttpServletRequest request, String endpointId, ProcessStatus status ) {
        setStatus( request, endpointId, status, null );
    }

    /**
     * Sets the status for the checking integrity process of a given enpoint in session
     *
     * @param request
     * @param endpointId
     * @param status
     * @param message
     */
    private void setStatus ( HttpServletRequest request, String endpointId, ProcessStatus status, String message ) {
        setStatus( request.getSession(), endpointId, status, message );
    }

    /**
     * Sets the status for the checking integrity process of a given enpoint in session
     *
     * @param session
     * @param endpointId
     * @param status
     * @param message
     */
    private void setStatus ( HttpSession session, String endpointId, ProcessStatus status, String message ) {
        session.setAttribute( "integrityCheck_" + endpointId, status );
        if ( message != null ) {
            session.setAttribute( "integrityCheck_message_" + endpointId, message );
        } else {
            session.removeAttribute( "integrityCheck_message_" + endpointId );
        }
    }

    /**
     * Removes the integrity checking thread from session and a given endpoint id
     *
     * @param request
     * @param endpointId
     */
    private void clearThreadInSession ( HttpServletRequest request, String endpointId ) {
        clearThreadInSession( request.getSession(), endpointId );
    }

    /**
     * Removes the integrity checking thread from session and a given endpoint id
     *
     * @param session
     * @param endpointId
     */
    private void clearThreadInSession ( HttpSession session, String endpointId ) {
        session.removeAttribute( "integrityThread_" + endpointId );
        session.removeAttribute( "integrityDataRequest_" + endpointId );
    }

    /**
     * Adds the integrity checking thread into the session for a given endpoint id
     *
     * @param request
     * @param thread
     * @param endpointId
     * @param integrityDataRequestID
     */
    private void addThreadToSession ( HttpServletRequest request, Thread thread, String endpointId, String integrityDataRequestID ) {
        addThreadToSession( request.getSession(), thread, endpointId, integrityDataRequestID );
    }

    /**
     * Adds the integrity checking thread into the session for a given endpoint id
     *
     * @param session
     * @param thread
     * @param endpointId
     * @param integrityDataRequestID
     */
    private void addThreadToSession ( HttpSession session, Thread thread, String endpointId, String integrityDataRequestID ) {
        session.setAttribute( "integrityThread_" + endpointId, thread );
        session.setAttribute( "integrityDataRequest_" + endpointId, integrityDataRequestID );
    }

    /**
     * Prepares a Response object with a given response text. The creation depends if it is an error or not.
     *
     * @param response
     * @param error
     * @return
     */
    private Response response ( String response, Boolean error ) {
        return response( response, error, "application/json" );
    }

    /**
     * Prepares a Response object with a given response text. The creation depends if it is an error or not.
     *
     * @param response
     * @param error
     * @param contentType
     * @return
     */
    private Response response ( String response, Boolean error, String contentType ) {
        if ( error ) {
            return Response.status( HttpStatus.SC_INTERNAL_SERVER_ERROR ).entity( response ).build();
        } else {
            return Response.ok( response, contentType ).build();
        }
    }

}
TOP

Related Classes of com.dotcms.rest.IntegrityResource

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.