package uws.service;
/*
* This file is part of UWSLibrary.
*
* UWSLibrary is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* UWSLibrary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with UWSLibrary. If not, see
This class implements directly the interface {@link UWS} and so, it represents the core of a UWS service.
* ** Using this class is very simple! An instance must be created by providing at a factory - {@link UWSFactory} - and a file manager - {@link UWSFileManager}. * This creation must be done in the init() function of a {@link HttpServlet}. Then, still in init(), at least one job list must be created. * Finally, in order to ensure that all requests are interpreted by the UWS service, they must be sent to the created {@link UWSService} in the function * {@link #executeRequest(HttpServletRequest, HttpServletResponse)}. *
*Here is an example of what should look like the servlet class:
*
* public class MyUWSService extends HttpServlet {
* private UWS uws;
*
* public void init(ServletConfig config) throws ServletException {
* try{
* // Create the UWS service:
* uws = new UWSService(new MyUWSFactory(), new LocalUWSFileManager(new File(config.getServletContext().getRealPath("UWSFiles"))));
* // Create at least one job list (otherwise no job can be started):
* uws.addJobList("jobList");
* }catch(UWSException ue){
* throw new ServletException("Can not initialize the UWS service!", ue);
* }
* }
*
* public void destroy(){
* if (uws != null)
* uws.destroy();
* }
*
* public void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException{
* try{
* service.executeRequest(request, response);
* }catch(UWSException ue){
* response.sendError(ue.getHttpErrorCode(), ue.getMessage());
* }
* }
* }
*
*
* * All standard UWS actions are already implemented in this class. However, it is still possible to modify their implementation and/or to * add or remove some actions. *
** A UWS action is actually implemented here by a class extending the abstract class {@link UWSAction}. Here is the full list of all * the available and already implemented actions: *
** To add an action, you should use the function {@link #addUWSAction(UWSAction)}, to remove one {@link #removeUWSAction(int)} or {@link #removeUWSAction(String)}. * Note that this last function takes a String parameter. This parameter is the name of the UWS action to remove. Indeed, each UWS action must have an internal * name representing the action. Thus, it is possible to replace a UWS action implementation by using the function {@link #replaceUWSAction(UWSAction)} ; this * function will replace the action having the same name as the given action. *
* ** In addition of all the actions listed above, a last action is automatically added: {@link ShowHomePage}. This is the action which will display the home page of * the UWS service. It is called when the root resource of the web service is asked. To change it, you can either overwrite this action * (see {@link #replaceUWSAction(UWSAction)}) or set an home page URL with the function {@link #setHomePage(String)} (the parameter is a URI pointing on either * a local or a remote resource) or {@link #setHomePage(URL, boolean)}. *
* * @author Grégory Mantelet (CDS;ARI) * @version 4.4 (08/2018) */ public class UWSService implements UWS { /** Name of this UWS. */ protected String name = null; /** Description of this UWS. */ protected String description = null; /** List of all managed jobs lists. (it is a LinkedHashMap so that jobs lists are ordered by insertion) */ protected final Map* If NULL, the standard strategy will be used: wait exactly the time asked * by the user (or indefinitely if none is specified). *
* @since 4.3 */ protected BlockingPolicy waitPolicy = null; /** Last generated request ID. If the next generated request ID is equivalent to this one, * a new one will generate in order to ensure the unicity. * @since 4.1 */ protected static String lastRequestID = null; /* ************ */ /* CONSTRUCTORS */ /* ************ */ /** *Builds a UWS (the base URI will be extracted at the first request directly from the request itself).
* ** By default, this UWS has 2 serialization formats: XML ({@link XMLSerializer}) and JSON ({@link JSONSerializer}). * All the default actions of a UWS are also already implemented. * However, you still have to create at least one job list ! *
* *note: since no logger is provided, a default one is set automatically (see {@link DefaultUWSLog}).
* * @param jobFactory Object which lets creating the UWS jobs managed by this UWS and their thread/task. * @param fileManager Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...). * * @throws NullPointerException If at least one of the parameters is null. * @throws UWSException If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}). * * @see #UWSService(UWSFactory, UWSFileManager, UWSLog) */ public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager) throws UWSException{ this(jobFactory, fileManager, (UWSLog)null); } /** *Builds a UWS (the base URI will be extracted at the first request directly from the request itself).
* ** By default, this UWS has 2 serialization formats: XML ({@link XMLSerializer}) and JSON ({@link JSONSerializer}). * All the default actions of a UWS are also already implemented. * However, you still have to create at least one job list ! *
* * @param jobFactory Object which lets creating the UWS jobs managed by this UWS and their thread/task. * @param fileManager Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...). * @param logger Object which lets printing any message (error, info, debug, warning). * * @throws NullPointerException If at least one of the parameters is null. * @throws UWSException If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}). */ public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSLog logger) throws UWSException{ if (jobFactory == null) throw new NullPointerException("Missing UWS factory! Can not create a UWSService."); factory = jobFactory; if (fileManager == null) throw new NullPointerException("Missing UWS file manager! Can not create a UWSService."); this.fileManager = fileManager; this.logger = (logger == null) ? new DefaultUWSLog(this) : logger; requestParser = jobFactory.createRequestParser(fileManager); errorWriter = new DefaultUWSErrorWriter(this.logger); // Initialize the list of jobs: mapJobLists = new LinkedHashMapBuilds a UWS with its base UWS URI.
* *note: since no logger is provided, a default one is set automatically (see {@link DefaultUWSLog}).
* * @param jobFactory Object which lets creating the UWS jobs managed by this UWS and their thread/task. * @param fileManager Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...). * @param baseURI Base UWS URI. * * @throws UWSException If the given URI is null or empty. * * @see #UWSService(UWSFactory, UWSFileManager, UWSLog, String) */ public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final String baseURI) throws UWSException{ this(jobFactory, fileManager, null, baseURI); } /** * Builds a UWS with its base UWS URI. * * @param jobFactory Object which lets creating the UWS jobs managed by this UWS and their thread/task. * @param fileManager Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...). * @param logger Object which lets printing any message (error, info, debug, warning). * @param baseURI Base UWS URI. * * @throws UWSException If the given URI is null or empty. * * @see UWSUrl#UWSUrl(String) */ public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSLog logger, final String baseURI) throws UWSException{ this(jobFactory, fileManager, logger); // Extract the name of the UWS: try{ // Set the URL interpreter: urlInterpreter = new UWSUrl(baseURI); // ...and the name of this service: name = urlInterpreter.getUWSName(); // Log the successful initialization: this.logger.logUWS(LogLevel.INFO, this, "INIT", "UWS successfully initialized!", null); }catch(NullPointerException ex){ // Log the exception: // (since the first constructor has already been called successfully, the logger is now NOT NULL): logger.logUWS(LogLevel.FATAL, null, "INIT", "Invalid base UWS URI: " + baseURI + "! You should check the configuration of the service.", ex); // Throw a new UWSException with a more understandable message: throw new UWSException(UWSException.BAD_REQUEST, ex, "Invalid base UWS URI (" + baseURI + ")!"); } } /** *Builds a UWS with the given UWS URL interpreter.
* *note: since no logger is provided, a default one is set automatically (see {@link DefaultUWSLog}).
* * @param jobFactory Object which lets creating the UWS jobs managed by this UWS and their thread/task. * @param fileManager Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...). * @param urlInterpreter The UWS URL interpreter to use in this UWS. * * @throws UWSException If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}). * * @see #UWSService(UWSFactory, UWSFileManager, UWSLog, UWSUrl) */ public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSUrl urlInterpreter) throws UWSException{ this(jobFactory, fileManager, null, urlInterpreter); } /** * Builds a UWS with the given UWS URL interpreter. * * @param jobFactory Object which lets creating the UWS jobs managed by this UWS and their thread/task. * @param fileManager Object which lets managing all files managed by this UWS (i.e. log, result, backup, error, ...). * @param logger Object which lets printing any message (error, info, debug, warning). * @param urlInterpreter The UWS URL interpreter to use in this UWS. * * @throws UWSException If unable to create a request parser using the factory (see {@link UWSFactory#createRequestParser(UWSFileManager)}). */ public UWSService(final UWSFactory jobFactory, final UWSFileManager fileManager, final UWSLog logger, final UWSUrl urlInterpreter) throws UWSException{ this(jobFactory, fileManager, logger); setUrlInterpreter(urlInterpreter); if (this.urlInterpreter != null) this.logger.logUWS(LogLevel.INFO, this, "INIT", "UWS successfully initialized.", null); } @Override public void destroy(){ // Backup all jobs: /* Jobs are backuped now so that running jobs are set back to the PENDING phase in the backup. * Indeed, the "stopAll" operation of the ExecutionManager may fail and would set the phase to ERROR * for the wrong reason. */ if (backupManager != null){ // save all jobs: backupManager.setEnabled(true); backupManager.saveAll(); // stop the automatic backup, if there is one: backupManager.setEnabled(false); } // Stop all jobs and stop watching for the jobs' destruction: for(JobList jl : mapJobLists.values()){ jl.getExecutionManager().stopAll(); jl.getDestructionManager().stop(); } // Just in case that previous clean "stop"s did not work, try again an interruption for all running threads: /* note: timers are not part of this ThreadGroup and so, they won't be affected by this function call. */ JobThread.tg.interrupt(); // Log the service is stopped: if (logger != null) logger.logUWS(LogLevel.INFO, this, "STOP", "UWS Service \"" + getName() + "\" stopped!", null); } /* ************** */ /* LOG MANAGEMENT */ /* ************** */ @Override public UWSLog getLogger(){ return logger; } /** * Gets the object used to write/format any error in a HttpServletResponse. * * @return The error writer/formatter. */ public final ServiceErrorWriter getErrorWriter(){ return errorWriter; } /** *Sets the object used to write/format any error in a HttpServletResponse.
* *Note: Nothing is done if the given writer is NULL !
* * @param errorWriter The new error writer/formatter. */ public final void setErrorWriter(ServiceErrorWriter errorWriter){ if (errorWriter != null) this.errorWriter = errorWriter; } /* ***************** */ /* GETTERS & SETTERS */ /* ***************** */ @Override public final String getName(){ return name; } /** * Sets the name of this UWS. * * @param name Its new name. */ public final void setName(String name){ this.name = name; } @Override public final String getDescription(){ return description; } /** * Sets the description of this UWS. * * @param description Its new description. */ public final void setDescription(String description){ this.description = description; } /** * Gets the base UWS URL. * * @return The base UWS URL. * * @see UWSUrl#getBaseURI() */ public final String getBaseURI(){ return (urlInterpreter == null) ? null : urlInterpreter.getBaseURI(); } @Override public final UWSUrl getUrlInterpreter(){ return urlInterpreter; } /** * Sets the UWS URL interpreter to use in this UWS. * * @param urlInterpreter Its new UWS URL interpreter (may be null. In this case, it will be created from the next request ; see {@link #executeRequest(HttpServletRequest, HttpServletResponse)}). */ public final void setUrlInterpreter(UWSUrl urlInterpreter){ this.urlInterpreter = urlInterpreter; if (name == null && urlInterpreter != null) name = urlInterpreter.getUWSName(); if (this.urlInterpreter != null) this.urlInterpreter.setUwsURI(null); } /** *Gets the object which lets extracting the user ID from a HTTP request.
*note:If the returned user identifier is NULL, no job should have an owner.
* * @return The used UserIdentifier (MAY BE NULL). */ @Override public final UserIdentifier getUserIdentifier(){ return userIdentifier; } /** * Sets the object which lets extracting the use ID from a received request. * * @param identifier The UserIdentifier to use (may be null). */ public final void setUserIdentifier(UserIdentifier identifier){ userIdentifier = identifier; } @Override public final UWSFactory getFactory(){ return factory; } @Override public final UWSFileManager getFileManager(){ return fileManager; } @Override public final UWSBackupManager getBackupManager(){ return backupManager; } /** ** Sets its backup manager. * This manager will be called at each user action to save only its own jobs list by calling {@link UWSBackupManager#saveOwner(JobOwner)}. *
* * @param backupManager Its new backup manager. */ public final void setBackupManager(final UWSBackupManager backupManager){ this.backupManager = backupManager; } @Override public final RequestParser getRequestParser(){ return requestParser; } /** * Get the currently used strategy for the blocking behavior of the * Job Summary action. * ** This strategy lets decide how long a WAIT request must block a HTTP * request. With a such policy, the waiting time specified by the user may * be modified. *
* * @return The WAIT strategy, * or NULL if the default one (i.e. wait the time specified by the * user) is used. * * @since 4.3 */ public final BlockingPolicy getWaitPolicy(){ return waitPolicy; } /** * Set the strategy to use for the blocking behavior of the * Job Summary action. * ** This strategy lets decide whether a WAIT request must block a HTTP * request and how long. With a such policy, the waiting time specified by * the user may be modified. *
* * @param waitPolicy The WAIT strategy to use, * or NULL if the default one (i.e. wait the time * specified by the user ; * if no time is specified the HTTP request may be * blocked indefinitely) must be used. * * @since 4.3 */ public final void setWaitPolicy(final BlockingPolicy waitPolicy){ this.waitPolicy = waitPolicy; } /* ******************** */ /* HOME PAGE MANAGEMENT */ /* ******************** */ /** * Gets the URL of the resource which must be used as home page of this UWS. * * @return The URL of the home page. */ public final String getHomePage(){ return homePage; } /** * Tells whether a redirection to the specified home page must be done or not. * * @return true if a redirection to the specified resource must be done * or false to copy it. */ public final boolean isHomePageRedirection(){ return homeRedirection; } /** * Sets the URL of the resource which must be used as home page of this UWS. * * @param homePageUrl The URL of the home page (may be null). * @param redirect true if a redirection to the specified resource must be done * or false to copy it. */ public final void setHomePage(URL homePageUrl, boolean redirect){ homePage = homePageUrl.toString(); homeRedirection = redirect; } /** *Sets the URI of the resource which must be used as home page of this UWS.
* A redirection will always be done on the specified resource. * * @param homePageURI The URL of the home page. */ public final void setHomePage(String homePageURI){ homePage = homePageURI; homeRedirection = true; } /** * Indicates whether the current home page is the default one (the UWS serialization) * or if it has been specified manually using {@link UWSService#setHomePage(URL, boolean)}. * * @return true if it is the default home page, false otherwise. */ public final boolean isDefaultHomePage(){ return homePage == null; } /** * Forgets the home page specified by using {@link UWSService#setHomePage(URL, boolean)} - if any - * and go back to the default home page (XML format). */ public final void setDefaultHomePage(){ homePage = null; homeRedirection = false; } /** *Get the MIME type of the custom home page.
* *By default, it is the same as the default home page: "text/html".
* *Note: * This function has a sense only if the HOME PAGE resource of this UWS service * is still the default home page (i.e. {@link ShowHomePage}). *
* * @return MIME type of the custom home page. * * @since 4.2 */ public final String getHomePageMimeType(){ return homePageMimeType; } /** *Set the MIME type of the custom home page.
* *A NULL value will be considered as "text/html".
* *Note: * This function has a sense only if the HOME PAGE resource of this UWS service * is still the default home page (i.e. {@link ShowHomePage}). *
* * @param mime MIME type of the custom home page. * * @since 4.2 */ public final void setHomePageMimeType(final String mime){ homePageMimeType = (mime == null || mime.trim().length() == 0) ? "text/html" : mime.trim(); } /* ********************** */ /* SERIALIZERS MANAGEMENT */ /* ********************** */ /** * Gets the MIME type of the serializer to use by default. * * @return The MIME type of the default serializer. */ public final String getDefaultSerializer(){ return defaultSerializer; } /** * Sets the MIME of the serializer to use by default. * * @param mimeType The MIME type (only one). * * @throws UWSException If there is no serializer with this MIME type available in this UWS. */ public final void setDefaultSerializer(String mimeType) throws UWSException{ if (serializers.containsKey(mimeType)) defaultSerializer = mimeType; else throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Missing UWS serializer for the MIME types: " + mimeType + "! The default serializer won't be set."); } /** *Adds a serializer to this UWS
*WARNING: If there is already a serializer with the same MIME type (see {@link UWSSerializer#getMimeType()}) in this UWS , * it should be replaced by the given one !
* * @param serializer The serializer to add. * @return true if the serializer has been successfully added, false otherwise. */ public final boolean addSerializer(UWSSerializer serializer){ if (serializer != null){ serializers.put(serializer.getMimeType(), serializer); if (serializers.size() == 1) defaultSerializer = serializer.getMimeType(); return true; } return false; } /** * Tells whether this UWS has already a serializer with the given MIME type. * * @param mimeType A MIME type (only one). * * @return true if a serializer exists with the given MIME type, false otherwise. */ public final boolean hasSerializerFor(String mimeType){ return serializers.containsKey(mimeType); } /** * Gets the total number of serializers available in this UWS. * * @return The number of its serializers. */ public final int getNbSerializers(){ return serializers.size(); } /** * Gets an iterator of the list of all serializers available in this UWS. * * @return An iterator on its serializers. */ public final IteratorLets adding the given action to this UWS.
* *WARNING: The action will be added at the end of the actions list of this UWS. That means, it will be evaluated (call of * the method {@link UWSAction#match(UWSUrl, JobOwner, HttpServletRequest)}) lastly !
* * @param action The UWS action to add. * * @return true if the given action has been successfully added, false otherwise. */ public final boolean addUWSAction(UWSAction action){ if (!uwsActions.contains(action)) return uwsActions.add(action); else return false; } /** *Lets inserting the given action at the given position in the actions list of this UWS.
* * @param indAction The index where the given action must be inserted. * @param action The action to add. * * @return true if the given action has been successfully added, false otherwise. * * @throws ArrayIndexOutOfBoundsException If the given index is incorrect (index < 0 || index >= uwsActions.size()). */ public final boolean addUWSAction(int indAction, UWSAction action) throws ArrayIndexOutOfBoundsException{ if (!uwsActions.contains(action)){ uwsActions.add(indAction, action); return true; } return false; } /** * Replaces the specified action by the given action. * * @param indAction Index of the action to replace. * @param action The replacer. * * @return true if the replacement has been a success, false otherwise. * * @throws ArrayIndexOutOfBoundsException If the index is incorrect (index < 0 || index >= uwsActions.size()). */ public final boolean setUWSAction(int indAction, UWSAction action) throws ArrayIndexOutOfBoundsException{ if (!uwsActions.contains(action)){ uwsActions.set(indAction, action); return true; } return false; } /** * Replaces the action which has the same name that the given action. * * @param action The replacer. * * @return The replaced action * or null if the given action is null * or if there is no action with the same name (in this case, the given action is not added). */ public final UWSAction replaceUWSAction(UWSAction action){ if (action == null) return null; else{ for(int i = 0; i < uwsActions.size(); i++){ if (uwsActions.get(i).equals(action)) return uwsActions.set(i, action); } return null; } } /** * Gets the number of actions this UWS has. * * @return The number of its actions. */ public final int getNbUWSActions(){ return uwsActions.size(); } /** * Gets the action of this UWS which has the same name as the given one. * * @param actionName The name of the searched action. * * @return The corresponding action * or null if there is no corresponding action. */ public final UWSAction getUWSAction(String actionName){ for(int i = 0; i < uwsActions.size(); i++){ if (uwsActions.get(i).getName().equals(actionName)) return uwsActions.get(i); } return null; } /** * Gets all actions of this UWS. * * @return An iterator on its actions. */ public final IteratorGenerate a unique ID for the given request.
* *By default, a timestamp is returned.
* * @param request Request whose an ID is asked. * * @return The ID of the given request. * * @since 4.1 */ protected synchronized String generateRequestID(final HttpServletRequest request){ String id; do{ id = System.currentTimeMillis() + ""; }while(lastRequestID != null && lastRequestID.startsWith(id)); lastRequestID = id; return id; } /** *Executes the given request according to the IVOA Proposed Recommendation of 2010-02-10. * The result is returned in the given response.
* *Here is the followed algorithm:
*Sends a redirection (with the HTTP status code 303) to the given URL/URI into the given response.
* * @param url The redirection URL/URI. * @param request The {@link HttpServletRequest} which may be used to make a redirection. * @param user The user which executes the given request. * @param uwsAction The UWS action corresponding to the given request. * @param response The {@link HttpServletResponse} which must contain all information to make a redirection. * * @throws IOException If there is an error during the redirection. * @throws UWSException If there is any other error. */ public void redirect(String url, HttpServletRequest request, JobOwner user, String uwsAction, HttpServletResponse response) throws IOException, UWSException{ response.setStatus(HttpServletResponse.SC_SEE_OTHER); response.setContentType(request.getContentType()); response.setCharacterEncoding(UWSToolBox.DEFAULT_CHAR_ENCODING); response.setHeader("Location", url); response.flushBuffer(); } /** ** Fills the response with the given error. The HTTP status code is set in function of the error code of the given UWSException. * If the error code is {@link UWSException#SEE_OTHER} this method calls {@link #redirect(String, HttpServletRequest, JobOwner, String, HttpServletResponse)}. * Otherwise the function {@link HttpServletResponse#sendError(int, String)} is called. *
* * @param error The error to send/display. * @param request The request which has caused the given error (not used by default). * @param reqID ID of the request. * @param user The user which executes the given request. * @param uwsAction The UWS action corresponding to the given request. * @param response The response in which the error must be published. * * @throws IOException If there is an error when calling {@link #redirect(String, HttpServletRequest, JobOwner, String, HttpServletResponse)} or {@link HttpServletResponse#sendError(int, String)}. * @throws UWSException If there is an error when calling {@link #redirect(String, HttpServletRequest, JobOwner, String, HttpServletResponse)}. * * @see #redirect(String, HttpServletRequest, JobOwner, String, HttpServletResponse) * @see #sendError(Throwable, HttpServletRequest, String, JobOwner, String, HttpServletResponse) */ public final void sendError(UWSException error, HttpServletRequest request, String reqID, JobOwner user, String uwsAction, HttpServletResponse response) throws IOException, UWSException{ if (error.getHttpErrorCode() == UWSException.SEE_OTHER){ // Log the redirection, if any: logger.logHttp(LogLevel.INFO, response, reqID, user, "HTTP " + UWSException.SEE_OTHER + " [Redirection toward " + error.getMessage() + "] - Action \"" + uwsAction + "\" successfully executed.", null); // Apply the redirection: redirect(error.getMessage(), request, user, uwsAction, response); }else sendError((Throwable)error, request, reqID, user, uwsAction, response); } /** ** Fills the response with the given error. * The stack trace of the error is printed on the standard output and then the function * {@link HttpServletResponse#sendError(int, String)} is called with the HTTP status code is {@link UWSException#INTERNAL_SERVER_ERROR} * and the message of the given exception. *
* * * @param error The error to send/display. * @param request The request which has caused the given error (not used by default). * @param reqID ID of the request. * @param user The user which executes the given request. * @param uwsAction The UWS action corresponding to the given request. * @param response The response in which the error must be published. * * @throws IOException If there is an error when calling {@link HttpServletResponse#sendError(int, String)}. * * @see ServiceErrorWriter#writeError(Throwable, HttpServletResponse, HttpServletRequest, String, JobOwner, String) */ public final void sendError(Throwable error, HttpServletRequest request, String reqID, JobOwner user, String uwsAction, HttpServletResponse response) throws IOException{ // Write the error in the response and return the appropriate HTTP status code: errorWriter.writeError(error, response, request, reqID, user, uwsAction); // Log the error: if (error instanceof UWSException) logger.logHttp(LogLevel.ERROR, response, reqID, user, "UWS action \"" + uwsAction + "\" FAILED with the error: \"" + error.getMessage() + "\"!", null); else logger.logHttp(LogLevel.FATAL, response, reqID, user, "UWS action \"" + uwsAction + "\" execution FAILED with a GRAVE error!", error); } }