package tap.backup; /* * This file is part of TAPLibrary. * * TAPLibrary 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. * * TAPLibrary 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 TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012,2014 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.json.Json4Uws; import tap.ExecutionProgression; import tap.TAPExecutionReport; import tap.TAPJob; import tap.parameters.DALIUpload; import uws.UWSException; import uws.job.UWSJob; import uws.service.UWS; import uws.service.backup.DefaultUWSBackupManager; import uws.service.log.UWSLog.LogLevel; import uws.service.request.UploadFile; /** * <p>Let backup all TAP asynchronous jobs.</p> * * <p><i>note: Basically the saved data are the same, but in addition some execution statistics are also added.</i></p> * * @author Grégory Mantelet (CDS;ARI) * @version 2.0 (12/2014) * * @see DefaultUWSBackupManager */ public class DefaultTAPBackupManager extends DefaultUWSBackupManager { /** * Build a default TAP jobs backup manager. * * @param uws The UWS containing all the jobs to backup. * * @see DefaultUWSBackupManager#DefaultUWSBackupManager(UWS) */ public DefaultTAPBackupManager(UWS uws){ super(uws); } /** * Build a default TAP jobs backup manager. * * @param uws The UWS containing all the jobs to backup. * @param frequency The backup frequency (in ms ; MUST BE positive and different from 0. * If negative or 0, the frequency will be automatically set to DEFAULT_FREQUENCY). * * @see DefaultUWSBackupManager#DefaultUWSBackupManager(UWS, long) */ public DefaultTAPBackupManager(UWS uws, long frequency){ super(uws, frequency); } /** * Build a default TAP jobs backup manager. * * @param uws The UWS containing all the jobs to backup. * @param byUser Backup mode. * * @see DefaultUWSBackupManager#DefaultUWSBackupManager(UWS, boolean) */ public DefaultTAPBackupManager(UWS uws, boolean byUser) throws UWSException{ super(uws, byUser); } /** * Build a default TAP jobs backup manager. * * @param uws The UWS containing all the jobs to backup. * @param byUser Backup mode. * @param frequency The backup frequency (in ms ; MUST BE positive and different from 0. * If negative or 0, the frequency will be automatically set to DEFAULT_FREQUENCY). * * @see DefaultUWSBackupManager#DefaultUWSBackupManager(UWS, boolean, long) */ public DefaultTAPBackupManager(UWS uws, boolean byUser, long frequency) throws UWSException{ super(uws, byUser, frequency); } @Override protected JSONObject getJSONJob(UWSJob job, String jlName) throws UWSException, JSONException{ JSONObject jsonJob = Json4Uws.getJson(job); // Re-Build the parameters map, by separating the uploads and the "normal" parameters: JSONArray uploads = new JSONArray(); JSONObject params = new JSONObject(); Object val; for(String name : job.getAdditionalParameters()){ // get the raw value: val = job.getAdditionalParameterValue(name); // if no value, skip this item: if (val == null) continue; // if an array, build a JSON array of strings: else if (val.getClass().isArray()){ JSONArray array = new JSONArray(); for(Object o : (Object[])val){ if (o != null && o instanceof DALIUpload) array.put(getDALIUploadJson((DALIUpload)o)); else if (o != null) array.put(o.toString()); } params.put(name, array); } // if upload file: else if (val instanceof UploadFile) uploads.put(getUploadJson((UploadFile)val)); // if DALIUpload: else if (val instanceof DALIUpload) params.put(name, getDALIUploadJson((DALIUpload)val)); // otherwise, just put the value: else params.put(name, val); } // Deal with the execution report of the job: if (job instanceof TAPJob && ((TAPJob)job).getExecReport() != null){ TAPExecutionReport execReport = ((TAPJob)job).getExecReport(); // Build the JSON representation of the execution report of this job: JSONObject jsonExecReport = new JSONObject(); jsonExecReport.put("success", execReport.success); jsonExecReport.put("uploadduration", execReport.getUploadDuration()); jsonExecReport.put("parsingduration", execReport.getParsingDuration()); jsonExecReport.put("executionduration", execReport.getExecutionDuration()); jsonExecReport.put("formattingduration", execReport.getFormattingDuration()); jsonExecReport.put("totalduration", execReport.getTotalDuration()); // Add the execution report into the parameters list: params.put("tapexecreport", jsonExecReport); } // Add the parameters and the uploads inside the JSON representation of the job: jsonJob.put(UWSJob.PARAM_PARAMETERS, params); jsonJob.put("uwsUploads", uploads); // Add the job owner: jsonJob.put(UWSJob.PARAM_OWNER, (job != null && job.getOwner() != null) ? job.getOwner().getID() : null); // Add the name of the job list owning the given job: jsonJob.put("jobListName", jlName); return jsonJob; } /** * Get the JSON representation of the given {@link DALIUpload}. * * @param upl The DALI upload specification to serialize in JSON. * * @return Its JSON representation. * * @throws JSONException If there is an error while building the JSON object. * * @since 2.0 */ protected JSONObject getDALIUploadJson(final DALIUpload upl) throws JSONException{ if (upl == null) return null; JSONObject o = new JSONObject(); o.put("label", upl.label); o.put("uri", upl.uri); o.put("file", (upl.file == null ? null : upl.file.paramName)); return o; } @Override protected void restoreOtherJobParams(JSONObject json, UWSJob job) throws UWSException{ // 0. Nothing to do in this function if the job is missing OR if it is not an instance of TAPJob: if (job == null || !(job instanceof TAPJob)) return; // 1. Build correctly the TAP UPLOAD parameter (the value of this parameter should be an array of DALIUpload): if (json != null && json.has(TAPJob.PARAM_PARAMETERS)){ try{ // Retrieve the whole list of parameters: JSONObject params = json.getJSONObject(TAPJob.PARAM_PARAMETERS); // If there is an UPLOAD parameter, convert the JSON array into a DALIUpload[] and add it to the job: if (params.has(TAPJob.PARAM_UPLOAD)){ // retrieve the JSON array: JSONArray uploads = params.getJSONArray(TAPJob.PARAM_UPLOAD); // for each item of this array, build the corresponding DALIUpload and add it into an ArrayList: DALIUpload upl; ArrayList<DALIUpload> lstTAPUploads = new ArrayList<DALIUpload>(); for(int i = 0; i < uploads.length(); i++){ try{ upl = getDALIUpload(uploads.getJSONObject(i), job); if (upl != null) lstTAPUploads.add(upl); }catch(JSONException je){ getLogger().logUWS(LogLevel.ERROR, uploads.get(i), "RESTORATION", "Incorrect JSON format for a DALIUpload of the job \"" + job.getJobId() + "\": a JSONObject was expected!", null); } } // finally convert the ArrayList into a DALIUpload[] and add it inside the parameters list of the job: job.addOrUpdateParameter(TAPJob.PARAM_UPLOAD, lstTAPUploads.toArray(new DALIUpload[lstTAPUploads.size()])); } }catch(JSONException ex){} } // 2. Get the execution report and add it into the given job: TAPJob tapJob = (TAPJob)job; Object obj = job.getAdditionalParameterValue("tapexecreport"); if (obj != null){ if (obj instanceof JSONObject){ JSONObject jsonExecReport = (JSONObject)obj; TAPExecutionReport execReport = new TAPExecutionReport(job.getJobId(), false, tapJob.getTapParams()); String[] keys = JSONObject.getNames(jsonExecReport); for(String key : keys){ try{ if (key.equalsIgnoreCase("success")) execReport.success = jsonExecReport.getBoolean(key); else if (key.equalsIgnoreCase("uploadduration")) execReport.setDuration(ExecutionProgression.UPLOADING, jsonExecReport.getLong(key)); else if (key.equalsIgnoreCase("parsingduration")) execReport.setDuration(ExecutionProgression.PARSING, jsonExecReport.getLong(key)); else if (key.equalsIgnoreCase("executionduration")) execReport.setDuration(ExecutionProgression.EXECUTING_ADQL, jsonExecReport.getLong(key)); else if (key.equalsIgnoreCase("formattingduration")) execReport.setDuration(ExecutionProgression.WRITING_RESULT, jsonExecReport.getLong(key)); else if (key.equalsIgnoreCase("totalduration")) execReport.setTotalDuration(jsonExecReport.getLong(key)); else getLogger().logUWS(LogLevel.WARNING, obj, "RESTORATION", "The execution report attribute '" + key + "' of the job \"" + job.getJobId() + "\" has been ignored because unknown!", null); }catch(JSONException je){ getLogger().logUWS(LogLevel.ERROR, obj, "RESTORATION", "Incorrect JSON format for the execution report serialization of the job \"" + job.getJobId() + "\" (attribute: \"" + key + "\")!", je); } } tapJob.setExecReport(execReport); }else if (!(obj instanceof JSONObject)) getLogger().logUWS(LogLevel.WARNING, obj, "RESTORATION", "Impossible to restore the execution report of the job \"" + job.getJobId() + "\" because the stored object is not a JSONObject!", null); } } /** * Restore a {@link DALIUpload} from its JSON representation. * * @param item {@link JSONObject} representing the {@link DALIUpload} to restore. * @param job The job which owns this upload. * * @return The corresponding {@link DALIUpload} or NULL, if an error occurs while converting the JSON. * * @since 2.0 */ private DALIUpload getDALIUpload(final JSONObject item, final UWSJob job){ try{ // Get its label: String label = item.getString("label"); // Build the DALIUpload object: /* If the upload spec. IS A FILE, the attribute 'file' should point toward a job parameter * being an UploadFile. If so, get it and use it to build the DALIUpload: */ if (item.has("file")){ Object f = job.getAdditionalParameterValue(item.getString("file")); if (f == null || !(f instanceof UploadFile)) getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect JSON format for the DALIUpload labelled \"" + label + "\" of the job \"" + job.getJobId() + "\": \"" + item.getString("file") + "\" is not pointing a job parameter representing a file!", null); return new DALIUpload(label, (UploadFile)f); } /* If the upload spec. IS A URI, the attribute 'uri' should contain it * and should be used to build the DALIUpload: */ else if (item.has("uri")){ try{ return new DALIUpload(label, new URI(item.getString("uri")), uws.getFileManager()); }catch(URISyntaxException e){ getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect URI for the DALIUpload labelled \"" + label + "\" of the job \"" + job.getJobId() + "\": \"" + item.getString("uri") + "\"!", null); } } /* If none of this both attribute is provided, it is an error and it is not possible to build the DALIUpload. */ else getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect JSON format for the DALIUpload labelled \"" + label + "\" of the job \"" + job.getJobId() + "\": missing attribute 'file' or 'uri'!", null); }catch(JSONException je){ getLogger().logUWS(LogLevel.ERROR, item, "RESTORATION", "Incorrect JSON format for a DALIUpload of the job \"" + job.getJobId() + "\": missing attribute 'label'!", null); } return null; } }