/** * Copyright 2014 SAP AG * * 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.spotter.service.rest; import java.io.IOException; import java.util.Set; import java.util.concurrent.ExecutionException; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.StreamingOutput; import org.lpe.common.config.ConfigParameterDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.spotter.service.SpotterServiceWrapper; import org.spotter.shared.configuration.ConfigKeys; import org.spotter.shared.configuration.JobDescription; import org.spotter.shared.configuration.SpotterExtensionType; import org.spotter.shared.hierarchy.model.XPerformanceProblem; import org.spotter.shared.service.ResponseStatus; import org.spotter.shared.service.SpotterServiceResponse; import org.spotter.shared.status.SpotterProgress; import com.sun.jersey.spi.resource.Singleton; /** * The service interface for Dynamic Spotter. * * @author Alexander Wert */ @Path(ConfigKeys.SPOTTER_REST_BASE) @Singleton public class SpotterService { private static final Logger LOGGER = LoggerFactory.getLogger(SpotterService.class); /** * Starts Dynamic Spotter diagnosis. * * @param jobDescription * job description object containing the whole DS setup such as * config values, environment and hierarchy configuration * @throws IOException * thrown if experiment fails * @return job id, 0 if already running */ @POST @Path(ConfigKeys.SPOTTER_REST_START_DIAG) @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Long> startDiagnosis(JobDescription jobDescription) throws IOException { try { long jobId = SpotterServiceWrapper.getInstance().startDiagnosis(jobDescription); if (jobId == 0) { return new SpotterServiceResponse<Long>(jobId, ResponseStatus.INVALID_STATE); } else { return new SpotterServiceResponse<Long>(jobId, ResponseStatus.OK); } } catch (Exception e) { return createErrorResponse(e); } } /** * Retrieves results from a Dynamic Spotter diagnosis that matches the given * job id. * * @param jobId * the job id matching to the diagnosis run to fetch the results * of * @return the results container for the given id or <code>null</code> if * for the id no results exist */ @POST @Path(ConfigKeys.SPOTTER_REST_REQU_RESULTS) @Consumes(MediaType.APPLICATION_JSON) @Produces("application/zip") public StreamingOutput requestResults(String jobId) { if (jobId == null) { return null; } try { return SpotterServiceWrapper.getInstance().requestResults(jobId); } catch (Exception e) { LOGGER.error("Server error: " + e); } return null; } /** * * @return true if spotter is currently running diagnosis */ @GET @Path(ConfigKeys.SPOTTER_REST_IS_RUNNING) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Boolean> isRunning() { try { Boolean isRunning = false; switch (SpotterServiceWrapper.getInstance().getState()) { case CANCELLED: case FINISHED: isRunning = false; break; case RUNNING: isRunning = true; break; default: isRunning = false; break; } return new SpotterServiceResponse<Boolean>(isRunning, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * * @return the exception thrown during the last diagnosis run or * <code>null</code> if none */ @GET @Path(ConfigKeys.SPOTTER_REST_LAST_EXCEPTION) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Exception> getLastRunException() { try { Exception lastRunException = null; switch (SpotterServiceWrapper.getInstance().getState()) { case CANCELLED: try { SpotterServiceWrapper.getInstance().checkForConcurrentExecutionException(); } catch (InterruptedException | ExecutionException e) { lastRunException = e; } break; default: break; } return new SpotterServiceResponse<>(lastRunException, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * * @return a set of configuration parameters for Dynamic Spotter. */ @GET @Path(ConfigKeys.SPOTTER_REST_CONFIG_PARAMS) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Set<ConfigParameterDescription>> getConfigurationParameters() { try { Set<ConfigParameterDescription> set = SpotterServiceWrapper.getInstance().getConfigurationParameters(); return new SpotterServiceResponse<Set<ConfigParameterDescription>>(set, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Returns a set of names of available extensions for the given type. * * @param extType * extension type * @return a set of extension names */ @GET @Path(ConfigKeys.SPOTTER_REST_EXTENSIONS + "/{extType}") @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Set<String>> getAvailableExtensions(@PathParam("extType") String extType) { try { SpotterExtensionType type = SpotterExtensionType.valueOf(extType); Set<String> set = SpotterServiceWrapper.getInstance().getAvailableExtensions(type); return new SpotterServiceResponse<Set<String>>(set, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Returns a set of available configuration parameters for the given * extension. * * @param extName * name of the extension of interest * @return list of configuration parameters */ @GET @Path(ConfigKeys.SPOTTER_REST_EXTENSION_PARAMETERS + "/{extName}") @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Set<ConfigParameterDescription>> getExtensionConfigParamters( @PathParam("extName") String extName) { try { Set<ConfigParameterDescription> set = SpotterServiceWrapper.getInstance().getExtensionConfigParamters( extName); return new SpotterServiceResponse<Set<ConfigParameterDescription>>(set, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Returns the default hierarchy. * * @return default hierarchy */ @GET @Path(ConfigKeys.SPOTTER_REST_DEFAULT_HIERARCHY) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<XPerformanceProblem> getDefaultHierarchy() { try { XPerformanceProblem root = SpotterServiceWrapper.getInstance().getDefaultHierarchy(); return new SpotterServiceResponse<XPerformanceProblem>(root, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Returns a report on the progress of the current job. * * @return progress report */ @GET @Path(ConfigKeys.SPOTTER_REST_CURRENT_PROGRESS) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<SpotterProgress> getCurrentProgressReport() { try { SpotterProgress progress = SpotterServiceWrapper.getInstance().getCurrentProgressReport(); return new SpotterServiceResponse<SpotterProgress>(progress, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Returns the id of the currently running job. * * @return id */ @GET @Path(ConfigKeys.SPOTTER_REST_CURRENT_JOB) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Long> getCurrentJobId() { try { Long id = SpotterServiceWrapper.getInstance().getCurrentJobId(); return new SpotterServiceResponse<Long>(id, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Returns the root problem of the currently running job. * * @return the root problem */ @GET @Path(ConfigKeys.SPOTTER_REST_CURRENT_ROOT_PROBLEM) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<XPerformanceProblem> getCurrentRootProblem() { try { XPerformanceProblem rootProblem = SpotterServiceWrapper.getInstance().getCurrentRootProblem(); return new SpotterServiceResponse<XPerformanceProblem>(rootProblem, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Tests connection to the satellite specified by the given extension name, * host and port. If extension is not a satellite this method returns false! * * @param extName * name of the extension to connect to * @param host * host / ip to connect to * @param port * port to connect to * @return true if connection could have been established, otherwise false */ @GET @Path(ConfigKeys.SPOTTER_REST_TEST_SATELLITE_CONNECTION + "/" + "{extName}" + "/" + "{host}" + "/" + "{port}") @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Boolean> testConnectionToSattelite(@PathParam(value = "extName") String extName, @PathParam(value = "host") String host, @PathParam(value = "port") String port) { try { Boolean connected = SpotterServiceWrapper.getInstance().testConnectionToSattelite(extName, host, port); return new SpotterServiceResponse<Boolean>(connected, ResponseStatus.OK); } catch (Exception e) { return createErrorResponse(e); } } /** * Tests connection to the Spotter service. * * @return true if connection could have been established, otherwise false */ @GET @Path(ConfigKeys.SPOTTER_REST_TEST_CONNECTION) @Produces(MediaType.APPLICATION_JSON) public SpotterServiceResponse<Boolean> testConnection() { return new SpotterServiceResponse<Boolean>(true, ResponseStatus.OK); } private <T> SpotterServiceResponse<T> createErrorResponse(Exception e) { SpotterServiceResponse<T> response = new SpotterServiceResponse<T>(null, ResponseStatus.SERVER_ERROR); LOGGER.error("Server error: " + e); if (e.getMessage() == null) { response.setErrorMessage(e.getClass().getSimpleName()); } else { response.setErrorMessage(e.getLocalizedMessage()); } return response; } }