use of au.gov.asd.tac.constellation.webserver.restapi.RestServiceException in project constellation by constellation-app.
the class RunPlugins method callService.
@Override
public void callService(final PluginParameters parameters, InputStream in, OutputStream out) throws IOException {
final String graphId = parameters.getStringValue(GRAPH_ID_PARAMETER_ID);
final Graph graph = graphId == null ? RestUtilities.getActiveGraph() : GraphNode.getGraph(graphId);
if (graph == null) {
throw new RestServiceException(HTTP_UNPROCESSABLE_ENTITY, "No graph with id " + graphId);
}
final String runStyle = parameters.getStringValue(RUN_IN_PARAMETER_ID);
if (!RUN_STYLE_SERIES.equals(runStyle) && !RUN_STYLE_PARALLEL.equals(runStyle)) {
final String msg = String.format("%s must be '%s' or '%s'", RUN_IN_PARAMETER_ID, RUN_STYLE_SERIES, RUN_STYLE_PARALLEL);
throw new RestServiceException(HTTP_UNPROCESSABLE_ENTITY, msg);
}
// First, collect all the plugins and their optional arguments.
//
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(in);
if (!json.isArray()) {
final String msg = String.format("Argument for %s must be a list", NAME);
throw new RestServiceException(HTTP_UNPROCESSABLE_ENTITY, msg);
}
final ConcurrentLinkedQueue<PluginError> errorQueue = new ConcurrentLinkedQueue<>();
final ArrayNode pluginList = (ArrayNode) json;
final List<PluginInstance> pluginInstances = new ArrayList<>();
pluginList.forEach(pluginItem -> {
if (!pluginItem.has(PLUGIN_NAME) || !pluginItem.get(PLUGIN_NAME).isTextual()) {
final String msg = String.format("Each plugin argument must have %s", PLUGIN_NAME);
throw new RestServiceException(HTTP_UNPROCESSABLE_ENTITY, msg);
}
final String pluginName = pluginItem.get(PLUGIN_NAME).textValue();
final Plugin plugin = PluginRegistry.get(pluginName);
final PluginParameters pluginParameters = plugin.createParameters();
if (pluginParameters != null && pluginItem.has(PLUGIN_ARGS)) {
final ObjectNode pluginArgs = (ObjectNode) pluginItem.get(PLUGIN_ARGS);
RestServiceUtilities.parametersFromJson(pluginArgs, pluginParameters);
}
pluginInstances.add(new PluginInstance(graph, plugin, pluginParameters, errorQueue));
});
//
if (runStyle.equals(RUN_STYLE_SERIES)) {
pluginInstances.forEach(PluginInstance::run);
} else {
final List<Thread> pluginThreads = new ArrayList<>();
pluginInstances.forEach(pi -> {
final Thread thread = new Thread(pi, pi.plugin.getName());
pluginThreads.add(thread);
thread.start();
});
pluginThreads.forEach(thread -> {
try {
thread.join();
} catch (final InterruptedException ex) {
thread.interrupt();
errorQueue.add(new PluginError(String.format("Thread %s", thread.getName()), ex));
}
});
}
// The plugins have finished, so look at the error queue to see if anything
// didn't work. If there are errors, pass them back.
//
final StringBuilder buf = new StringBuilder();
while (!errorQueue.isEmpty()) {
buf.append(errorQueue.remove().toString());
}
if (buf.length() > 0) {
throw new RestServiceException(buf.toString());
}
}
use of au.gov.asd.tac.constellation.webserver.restapi.RestServiceException in project constellation by constellation-app.
the class RestServiceServlet method callService.
private void callService(final HttpMethod httpMethod, final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
// Which service is being called?
//
final String serviceName = request.getPathInfo().substring(1);
// Get an instance of the service (if it exists).
//
final RestService rs = RestServiceRegistry.get(serviceName, httpMethod);
// Convert the arguments in the URL of the request to PluginParameters.
//
final PluginParameters parameters = rs.createParameters();
final Map<String, String[]> paramMap = request.getParameterMap();
paramMap.entrySet().forEach(entry -> {
final String parameterName = entry.getKey();
if (parameters.hasParameter(parameterName)) {
final PluginParameter<?> param = parameters.getParameters().get(parameterName);
if (entry.getValue().length == 1) {
param.setStringValue(entry.getValue()[0]);
} else {
throw new RestServiceException("Service parameters do not accept multiple values");
}
} else {
throw new RestServiceException(String.format("Service '%s' has no parameter '%s'", serviceName, parameterName));
}
});
//
try {
response.setContentType(rs.getMimeType());
response.setStatus(HttpServletResponse.SC_OK);
rs.callService(parameters, request.getInputStream(), response.getOutputStream());
} catch (final RestServiceException ex) {
throw ex;
} catch (final IOException | RuntimeException ex) {
throw new ServletException(ex);
}
}
use of au.gov.asd.tac.constellation.webserver.restapi.RestServiceException in project constellation by constellation-app.
the class SetGraphValues method callService.
@Override
public void callService(final PluginParameters parameters, InputStream in, OutputStream out) throws IOException {
final String graphId = parameters.getStringValue(GRAPH_ID_PARAMETER_ID);
final Graph graph = graphId == null ? RestUtilities.getActiveGraph() : GraphNode.getGraph(graphId);
if (graph == null) {
throw new RestServiceException(HTTP_UNPROCESSABLE_ENTITY, "No graph with id " + graphId);
}
// We want to read a JSON document that looks like:
//
// {"columns":["A","B"],"data":[[1,"a"]]}
//
// which is what is output by pandas.to_json(..., orient="split').
// (We ignore the index array.)
final ObjectMapper mapper = new ObjectMapper();
final JsonNode json = mapper.readTree(in);
if (!json.hasNonNull(COLUMNS) || !json.get(COLUMNS).isArray()) {
throw new RestServiceException(HTTP_UNPROCESSABLE_ENTITY, "Could not find columns object containing column names");
}
if (!json.hasNonNull("data") || !json.get("data").isArray()) {
throw new RestServiceException(HTTP_UNPROCESSABLE_ENTITY, "Could not find data object containing data rows");
}
final ArrayNode columns = (ArrayNode) json.get(COLUMNS);
final ArrayNode data = (ArrayNode) json.get("data");
// Do we have one and only one row of data?
if (data.size() != 1) {
throw new RestServiceException("Must have one row of data");
}
final ArrayNode row = (ArrayNode) data.get(0);
// Do the number of column headers and the number of data elements in the row match?
if (columns.size() != row.size()) {
throw new RestServiceException("Column names do not match data row");
}
setGraphAttributes(graph, columns, row);
}
use of au.gov.asd.tac.constellation.webserver.restapi.RestServiceException in project constellation by constellation-app.
the class SetGraphValues method setGraphAttributes.
private static void setGraphAttributes(final Graph graph, final ArrayNode columns, final ArrayNode row) {
final Plugin p = new SetGraphAttributesFromRestApiPlugin(columns, row);
final PluginExecution pe = PluginExecution.withPlugin(p);
try {
pe.executeNow(graph);
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new RestServiceException(ex);
} catch (final PluginException ex) {
throw new RestServiceException(ex);
}
}
use of au.gov.asd.tac.constellation.webserver.restapi.RestServiceException in project constellation by constellation-app.
the class FileListener method run.
/**
* Run the file listener thread.
* <p>
* Poll the listener directory looking for the REQUEST_JSON file. It assumed
* that the client has already written the CONTENT_IN file if required.
* <p>
* When REQUEST_JSON is found, read and extract the verb + endpoint + path +
* args. Call the multi-level switch statement that figures out what to do.
* Some of these will write CONTENT_OUT. Ensure that CONTENT_IN is deleted
* when we've finished with it. Ensure that REQUEST_JSON is deleted when
* we're finished with it. Last of all, write the RESPONSE_JSON file: this
* is what the client will be polling on. An empty JSON document implies
* success. A JSON document with the "error" key is failure, with the
* explanation in the value. The client will delete RESPONSE_JSON and
* CONTENT_OUT.
*/
@Override
public void run() {
// Download the Python REST client if enabled.
final Preferences prefs = NbPreferences.forModule(ApplicationPreferenceKeys.class);
final boolean pythonRestClientDownload = prefs.getBoolean(ApplicationPreferenceKeys.PYTHON_REST_CLIENT_DOWNLOAD, ApplicationPreferenceKeys.PYTHON_REST_CLIENT_DOWNLOAD_DEFAULT);
if (pythonRestClientDownload) {
WebServer.downloadPythonClient();
}
StatusDisplayer.getDefault().setStatusText(String.format("Starting file listener in directory %s", restPath));
running = true;
// We start the poll sleep time at MIN_POLL_SLEEP.
// If the listener doesn't find any files, the sleep time will slowly get longer up to MAX_POLL_SLEEP,
// so the filesystem doesn't get pounded as much.
// When a request is found, the poll sleep time is reset to MIN_POLL_SLEEP.
long pollSleep = MIN_POLL_SLEEP;
try {
while (running) {
final String[] files = restPath.toFile().list();
for (final String f : files) {
if (f.equals(REQUEST_JSON)) {
// If any other files are required, write this file last, so the other files are already present.
LOGGER.info(String.format("Found REST file %s", f));
final Path p = restPath.resolve(f);
JsonNode json = null;
try (final InputStream in = new FileInputStream(p.toFile())) {
final ObjectMapper mapper = new ObjectMapper();
json = mapper.readTree(in);
} catch (final IOException ex) {
response(ex.getMessage());
}
try {
Files.delete(p);
} catch (final IOException ex) {
LOGGER.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
}
if (json != null) {
// If content (JSON or otherwise) is required, it gets delivered in a separate CONTENT_DATA file.
if (json.hasNonNull("verb") && json.hasNonNull(ENDPOINT) && json.hasNonNull("path")) {
final String verb = json.get("verb").textValue();
final String endpoint = json.get(ENDPOINT).textValue();
final String path = json.get("path").textValue();
final JsonNode args = json.get("args");
try {
// Display the incoming REST request to provide some confidence to the user and debugging for the developer :-).
final String msg = String.format("File REST API: %s %s %s", verb, endpoint, path);
StatusDisplayer.getDefault().setStatusText(msg);
parseAndExecute(verb, endpoint, path, args);
response();
} catch (final RestServiceException ex) {
response(ex.getMessage());
} catch (final Exception ex) {
response(ex.getMessage());
LOGGER.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
}
} else {
response("Request must contain verb + endpoint + path");
}
}
// Reset the poll sleep after a successful request,
// since we're obviously being used.
pollSleep = MIN_POLL_SLEEP;
}
}
// Slowly sneak the poll sleep time up to a maximum.
pollSleep = Math.min(pollSleep + 1, MAX_POLL_SLEEP);
Thread.sleep(pollSleep);
}
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
stop();
LOGGER.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
}
StatusDisplayer.getDefault().setStatusText(String.format("Stopped file listener in directory %s", restPath));
}
Aggregations