Commit 73d9cf9a authored by bruse's avatar bruse
Browse files

Import job files can be sent now. I had to restructure the job status handling...

Import job files can be sent now. I had to restructure the job status handling and introduced new job classes.
parent 54a72b8d
package eu.simstadt.nf;
import java.util.Objects;
/**
* Export jobs are requests for CityGML models. Every valid export job has an id and a status.
*
* @author Marcel Bruse
*/
public class ExportJob extends Job {
/**
* Initializes a new export job instance with unknown state. This is a convenient method.
*
* @return Returns a new export job instance with unknown state.
*/
public static ExportJob getUnkownInstance() {
return new ExportJob(JobStatus.UNKOWN);
}
/**
* This constructor forces the job to have a defined state.
*
* @param status
*/
public ExportJob(JobStatus status) {
super(status);
}
/**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent
* to you in http responses.
*
* @param statusCode The nF status code for this job.
*/
@Override
public void setStatusForCode(int statusCode) {
switch (statusCode) {
case 0:
setStatus(JobStatus.PENDING); break;
case 10:
setStatus(JobStatus.RUNNING); break;
case 20:
setStatus(JobStatus.FAILED); break;
case 30:
setStatus(JobStatus.FINISHED); break;
default:
setStatus(JobStatus.UNKOWN);
}
}
/**
* @return Returns true, if the job is definitely done. False, otherwise.
*/
public boolean isFinished() {
return Objects.nonNull(getStatus()) && getStatus().equals(JobStatus.FINISHED);
}
}
package eu.simstadt.nf;
/**
* Import jobs are requests to store, change or delete CityGML models. Every valid import job has an id and a status.
*
* @author Marcel Bruse
*/
public class ImportJob extends Job {
/**
* Initializes a new import job instance with unknown state. This is a convenient method.
*
* @return Returns a new import job instance with unknown state.
*/
public static ImportJob getUnkownInstance() {
return new ImportJob(JobStatus.UNKOWN);
}
/**
* This constructor forces the job to have a defined state.
*
* @param status
*/
public ImportJob(JobStatus status) {
super(status);
}
/**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent
* to you in http responses.
*
* @param statusCode The nF status code for this job.
*/
@Override
public void setStatusForCode(int statusCode) {
switch (statusCode) {
case 0:
setStatus(JobStatus.READY_TO_RUN); break;
case 10:
setStatus(JobStatus.RUNNING); break;
case 20:
setStatus(JobStatus.ERROR); break;
case 25:
setStatus(JobStatus.WARNING); break;
case 30:
setStatus(JobStatus.FINISHED); break;
case 40:
setStatus(JobStatus.APPROVE); break;
case 45:
setStatus(JobStatus.REJECT); break;
case 50:
setStatus(JobStatus.APPROVE_RUNNING); break;
case 55:
setStatus(JobStatus.REJECT_RUNNING); break;
case 60:
setStatus(JobStatus.APPROVE_REJECT_ERROR); break;
case 70:
setStatus(JobStatus.APPROVE_REJECT_OK); break;
case 80:
setStatus(JobStatus.IMPORTED_WARNING); break;
default:
setStatus(JobStatus.UNKOWN);
}
}
}
package eu.simstadt.nf;
/**
* This job class bundles the three attributes of every nF job: Id, status and last job related nF (error) message.
*
* @author Marcel Bruse
*/
public abstract class Job {
/** The status of the job. There are different states for export and import jobs. */
private JobStatus status;
/** The id of the job. */
private int jobId;
/**
* This constructor forces the job to have a defined state.
*
* @param status The initial status of this job.
*/
public Job(JobStatus status) {
this.status = status;
}
/**
* Every job has it's status. Look up the different possible values in the JobStatus enumeration.
*
* @return Returns the status of this job.
*/
public JobStatus getStatus() {
return status;
}
/**
* Lets you set the status of this job.
*
* @param status The status of this job.
*/
protected void setStatus(JobStatus status) {
this.status = status;
}
/**
* @return Returns the id of this job.
*/
public int getJobId() {
return jobId;
}
/**
* Sets the id of this job.
*
* @param jobId The job id about to be set.
*/
public void setJobId(int jobId) {
this.jobId = jobId;
}
/**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent
* to you in http responses. Export jobs and import jobs have different states. Consider this fact
* in your implementation.
*
* @param statusCode The nF status code for this job.
*/
public abstract void setStatusForCode(int statusCode);
}
package eu.simstadt.nf;
/**
* Enumeration of possible states of nF import and export jobs.
* The list of all possible export and import job states.
*
* @author Marcel Bruse
*/
public enum JobStatus {
UNKOWN(-1), PENDING(0), RUNNING(10), FAILED(20), FINISHED(30);
UNKOWN(0), PENDING(1), RUNNING(2), FAILED(3), FINISHED(4), READY_TO_RUN(5), ERROR(6), WARNING(7), APPROVE(8),
REJECT(9), APPROVE_RUNNING(10), REJECT_RUNNING(11), APPROVE_REJECT_ERROR(12), APPROVE_REJECT_OK(13),
IMPORTED_WARNING(14);
public static final String UNKNOWN_STATUS_MESSAGE = "The state of the job is unknown.";
public static final String PENDING_MESSAGE = "Job is pending.";
......@@ -14,61 +16,49 @@ public enum JobStatus {
public static final String FAILED_MESSAGE = "Job failed.";
public static final String FINISHED_MESSAGE = "Job is finished.";
private int jobId;
private int value;
private String message;
private JobStatus(int value) {
this.value = value;
if (value == 0) {
message = PENDING_MESSAGE;
} else if (value == 10) {
message = RUNNING_MESSAGE;
} else if (value == 20) {
message = FAILED_MESSAGE;
} else if (value == 30) {
message = FINISHED_MESSAGE;
} else {
message = UNKNOWN_STATUS_MESSAGE;
/**
* This constructor sets messages for some states.
*
* @param internalCode Our numerical SimStadt-internal code for the nF status codes.
*/
private JobStatus(int internalCode) {
switch (internalCode) {
case 0:
message = UNKNOWN_STATUS_MESSAGE; break;
case 1:
case 5:
message = PENDING_MESSAGE; break;
case 2:
message = RUNNING_MESSAGE; break;
case 3:
case 6:
message = FAILED_MESSAGE; break;
case 4:
message = FINISHED_MESSAGE; break;
default:
message = "";
}
}
public static JobStatus getInstanceForStatus(int status) {
if (status == 0) {
return PENDING;
} else if (status == 10) {
return RUNNING;
} else if (status == 20) {
return FAILED;
} else if (status == 30) {
return FINISHED;
} else {
return UNKOWN;
}
}
public int getValue() {
return value;
}
public int getJobId() {
return jobId;
}
public void setJobId(int jobId) {
this.jobId = jobId;
}
/**
* This message describes the status or gives hints about error states. This message may come from a
* nF response.
*/
private String message;
/**
* Sets a message for this status in order to describe the status.
*
* @param message A message to describe this status.
*/
public void setMessage(String message) {
this.message = message;
}
/**
* @return Returns a message which describes this status.
*/
public String getMessage() {
return message;
}
public boolean isFinished() {
return this == FINISHED;
}
}
package eu.simstadt.nf;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
NFConnector connector = new NFConnectorImpl("193.196.136.164");
JobStatus status = connector.requestJobStatus(445);
System.out.println(status + " - " + status.getMessage());
if (status.isFinished()) {
File file = connector.requestJobResult(445);
System.out.println(file.canRead());
}
ImportJob job = connector.requestImportJob(261);
JobStatus status = job.getStatus();
System.out.println(job.getJobId() + ": " + status + " - " + status.getMessage());
}
}
\ No newline at end of file
......@@ -15,7 +15,7 @@ public interface NFConnector {
*
* The various NovaFACTORY interfaces may change over time. Such changes force the SimStadt programmers to
* implement different versions of this NFConnector interface while keeping old implementations due to
* backward comparability.
* backward compatibility.
*
* @return The supported version of the novaFACTORY.
*/
......@@ -28,15 +28,51 @@ public interface NFConnector {
* @param file The nF export job as a prepared XML file.
* @return The status of the job. Could be pending or failed.
*/
public JobStatus sendExportJob(File file);
public ExportJob sendExportJobFile(File exportJobFile);
/**
* Returns the status of any existing nF job.
* Sends an import job to nF. The given import job file has to be a zip file and has to obey the internal
* structure defined in the nF manuals. Short description:
*
* @param jobId The id of the job for which you want to request the status.
* @return The status of any existing nF job.
* Import jobs enable you to add, alter and delete CityGML top level objects (like buildings) in the nF.
* Every import job file has to contain a start file. Start files control the import process through their
* file names and their contents. The name of a start file has the following structure
*
* <Product>_<Tile>.start
*
* The start file should contain the level to which your manipulated CityGML should be imported. Your CityGML
* file has to be named after the following naming convention:
*
* <Product>_<Tile>_<Level>_<Operation>.gml
*
* Here operation can be ...
* - 'REP': Replaces whole existing buildings only,
* - 'REPUPD': Replaces whole existing buildings and adds new buildings,
* - 'UPD': Update, same as REP,
* - 'CHG': Change, same as REPUPD,
* - 'DEL': Deletes the geometry of a particular LOD,
* - 'DELALL': Deletes a whole building
*
* @param file The nF import job as a prepared ZIP file. It has to contain the CityGML file and the start file.
* @return Returns the job id of your import job or -1 if something went wrong.
*/
public ImportJob sendImportJobFile(File importJobFile);
/**
* Returns the status of any existing export nF job.
*
* @param jobId The id of the export job for which you want to request the status.
* @return The status of any existing export nF job.
*/
public ExportJob requestExportJob(int jobId);
/**
* Returns the status of any existing import nF job.
*
* @param jobId The id of the import job for which you want to request the status.
* @return The status of any existing import nF job.
*/
public JobStatus requestJobStatus(int jobId);
public ImportJob requestImportJob(int jobId);
/**
* Downloads the result for a given nF export job and hands over the corresponding file handle.
......@@ -44,6 +80,6 @@ public interface NFConnector {
* @param jobId The id of the export job for which the result should be loaded.
* @return A file handle to the result of the nF export job.
*/
public File requestJobResult(int jobId);
public File requestExportJobResult(int jobId);
}
......@@ -125,24 +125,49 @@ public String supportsNFVersion() {
}
/**
* Returns the status of any existing nF job.
* Returns the status of any existing nF export job.
*
* @param jobId The id of the job for which you want to request the status.
* @return The status of any existing nF job.
* @param jobId The id of the export job for which you want to request the status.
* @return The status of any existing nF export job.
* @throws IOException An error occurred during the request. This could be a malformed URL or a network failure.
*/
@Override
public JobStatus requestJobStatus(int jobId) {
JobStatus result = JobStatus.UNKOWN;
public ExportJob requestExportJob(int jobId) {
ExportJob result = ExportJob.getUnkownInstance();
try {
List<String> parameters = Arrays.asList(buildParameter("jobid", jobId));
result = getJobStatusFromResponse(getResponse(buildURL(REMOTE_STATUS_SERVLET, parameters)));
getJobFromResponse(result, getResponse(buildURL(REMOTE_STATUS_SERVLET, parameters)));
} catch (MalformedURLException ex) {
result.setMessage(MALFORMED_URL);
result.getStatus().setMessage(MALFORMED_URL);
} catch (IOException ex) {
result.setMessage(IO_EXCEPTION_OCCURRED);
result.getStatus().setMessage(IO_EXCEPTION_OCCURRED);
} catch (ParserConfigurationException | SAXException ex) {
result.setMessage(XML_PARSER_ERROR);
result.getStatus().setMessage(XML_PARSER_ERROR);
}
return result;
}
/**
* Returns the status of any existing nF import job.
*
* @param jobId The id of the import job for which you want to request the status.
* @return The status of any existing nF import job.
* @throws IOException An error occurred during the request. This could be a malformed URL or a network failure.
*/
@Override
public ImportJob requestImportJob(int jobId) {
ImportJob result = ImportJob.getUnkownInstance();
try {
List<String> parameters = Arrays.asList(
buildParameter("jobid", jobId),
buildParameter("request", "status"));
getJobFromResponse(result, getResponse(buildURL(REMOTE_IMPORT_SERVLET, parameters)));
} catch (MalformedURLException ex) {
result.getStatus().setMessage(MALFORMED_URL);
} catch (IOException ex) {
result.getStatus().setMessage(IO_EXCEPTION_OCCURRED);
} catch (ParserConfigurationException | SAXException ex) {
result.getStatus().setMessage(XML_PARSER_ERROR);
}
return result;
}
......@@ -154,7 +179,7 @@ public JobStatus requestJobStatus(int jobId) {
* @return A file handle to the result of the nF export job.
*/
@Override
public File requestJobResult(int jobId) {
public File requestExportJobResult(int jobId) {
File result = null;
try {
List<String> parameters = Arrays.asList(
......@@ -246,6 +271,11 @@ private String getResponse(HttpURLConnection httpConnection) throws UnsupportedE
} else {
throw new IOException();
}
System.out.println(xml);
return xml;
}
......@@ -255,35 +285,38 @@ private String getResponse(HttpURLConnection httpConnection) throws UnsupportedE
* manual for more information about the XML reports and have a look at the DTD of the XML reports.
*
* @param xml A XML string that is supposed to be a XML document.
* @return The status of the job which was encoded in the given XML string.
* @param An empty job status object about to be filled with the actual result. It will be UNKOWN if there was
* no XML report in the response.
* @throws ParserConfigurationException Something went wrong.
* @throws SAXException Some parse error.
* @throws IOException Some parse error.
*/
private JobStatus getJobStatusFromResponse(String xml)
private void getJobFromResponse(Job job, String xml)
throws ParserConfigurationException, SAXException, IOException {
JobStatus result = JobStatus.UNKOWN;
JobStatus status = JobStatus.UNKOWN;
job.setStatus(status);
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
SAXParser parser = saxFactory.newSAXParser();
StringReader reader = new StringReader(xml);
ReportHandler handler = new ReportHandler();
parser.parse(new InputSource(reader), handler);
if (Objects.nonNull(handler.serviceException)) {
result.setMessage(handler.serviceException);
} else if (Objects.nonNull(handler.statusId)) {
result = JobStatus.getInstanceForStatus(handler.statusId);
if (Objects.nonNull(handler.statusId)) {
job.setStatusForCode(handler.statusId);
}
if (Objects.nonNull(handler.serviceException)) {
status.setMessage(handler.serviceException);
}
if (Objects.nonNull(handler.jobId)) {
result.setJobId(handler.jobId);
job.setJobId(handler.jobId);
}
return result;
}
/**
* Downloads a (zipped) CityGML file from the nF server. The file will be the result of an export job.
*
* @param url The URL of the remote order servlet that points at the CityGML file on the nF server.
* @return Returns a handle to the downloaded (zipped) CityGML file.
* @return Returns a handle to the downloaded (zipped) CityGML file. Will be null if no file has been
* downloaded (due to errors).
* @throws IOException Something went wrong with your data connection. Check your server and the port.
*/
private File downloadFile(URL url) throws IOException {
......@@ -325,52 +358,95 @@ private File downloadFile(URL url) throws IOException {
* @return Returns the status of the export job you just sent.
*/
@Override
public JobStatus sendExportJob(File file) {
JobStatus result = JobStatus.UNKOWN;
public ExportJob sendExportJobFile(File file) {
ExportJob result = ExportJob.getUnkownInstance();
try {
URL url = buildURL(REMOTE_ORDER_SERVLET, null);
JobStatus status = sendFile(url, file);
if (Objects.nonNull(status.getJobId()) && status.getJobId() > 0) {
status = requestJobStatus(status.getJobId());
} else if (Objects.nonNull(status.getMessage()) && status.getMessage().isEmpty()) {
status.setMessage(UNKNOWN_ERROR_OCCURRED);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setDoOutput(true); // Sends export job file
connection.setDoInput(true); // Expects XML response
connection.connect();
OutputStream os = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, DEFAULT_CHARSET), true);
Files.copy(file.toPath(), os);
os.flush();
writer.append(CRLF).flush();
getJobFromResponse(result, getResponse(connection));
// At this line, result will be UNKNOWN, although the job has been enqueued. A separate status request
// will be sent in order to query for the actual state of the job.
if (result.getJobId() > 0) {
result = requestExportJob(result.getJobId());
}
result = status;
} catch (MalformedURLException ex) {
result.setMessage(MALFORMED_URL);
result.getStatus().setMessage(MALFORMED_URL);
} catch (IOException ex) {
result.setMessage(IO_EXCEPTION_OCCURRED);
result.getStatus().setMessage(IO_EXCEPTION_OCCURRED);
} catch (ParserConfigurationException | SAXException ex) {
result.setMessage(XML_PARSER_ERROR);
result.getStatus().setMessage(XML_PARSER_ERROR);
}
return result;
}
/**
* Does the actual job for the sendExportJob() method. sendExportJob() builds the URL for the remote servlet
* and calls this method.
* Sends an import job to nF. The given import job file has to be a zip file and has to obey the internal
* structure defined in the nF manuals. Short description:
*
* @param url The URL of the remote order servlet to which the file should be send to.
* @param file The file to be sent to nF.
* @return Returns the status of the export job you just sent.
* @throws IOException There was a problem with your data connection.
* @throws ParserConfigurationException There was a problem while parsing the response.
* @throws SAXException Some parse error while parsing the response.
* Import jobs enable you to add, alter and delete CityGML top level objects (like buildings) in the nF.
* Every import job file has to contain a start file. Start files control the import process through their
* file names and their contents. The name of a start file has the following structure
*
* <Product>_<Tile>.start
*
* The start file should contain the level to which your manipulated CityGML should be imported. Your CityGML
* file has to be named after the following naming convention:
*
* <Product>_<Tile>_<Level>_<Operation>.gml
*
* Here operation can be ...
* - 'REP': Replaces whole existing buildings only,
* - 'REPUPD': Replaces whole existing buildings and adds new buildings,
* - 'UPD': Update, same as REP,
* - 'CHG': Change, same as REPUPD,
* - 'DEL': Deletes the geometry of a particular LOD,
* - 'DELALL': Deletes a whole building
*
* @param file The nF import job as a prepared ZIP file. It has to contain the CityGML file and the start file.
* @return Returns the job status of your import job.
*/
private JobStatus sendFile(URL url, File file) throws IOException, ParserConfigurationException, SAXException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setDoOutput(true); // Sends export job file
connection.setDoInput(true); // Expects XML response
connection.connect();
OutputStream os = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, DEFAULT_CHARSET), true);
Files.copy(file.toPath(), os);
os.flush();
writer.append(CRLF).flush();
return getJobStatusFromResponse(getResponse(connection));
@Override
public ImportJob sendImportJobFile(File importJobFile) {
ImportJob result = ImportJob.getUnkownInstance();
try {
List<String> parameters = Arrays.asList(
buildParameter("request", "imp"),
buildParameter("pdctKrz", "LBTEST"),
buildParameter("createjob", "1"));
URL url = buildURL(REMOTE_IMPORT_SERVLET, parameters);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/zip");
connection.setRequestProperty("Accept", "*/*");
connection.setRequestProperty("", "");
connection.setDoOutput(true); // Sends export job file
connection.setDoInput(true); // Expects XML response
connection.connect();
OutputStream os = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, DEFAULT_CHARSET), true);
Files.copy(importJobFile.toPath(), os);
os.flush();
writer.append(CRLF).flush();
getJobFromResponse(result, getResponse(connection));
} catch (MalformedURLException ex) {
result.getStatus().setMessage(MALFORMED_URL);
} catch (IOException ex) {
result.getStatus().setMessage(IO_EXCEPTION_OCCURRED);
} catch (SAXException | ParserConfigurationException ex) {
result.getStatus().setMessage(XML_PARSER_ERROR);
}
return result;
}
}
\ No newline at end of file
package eu.simstadt.nf;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
......@@ -11,9 +12,15 @@
*/
public class ReportHandler extends DefaultHandler {
/** The XML tag which tells you the status of a nF job. */
/** The XML tag which tells you the status of a nF export job. */
public static final String STATUS_TAG = "status";
/** The XML tag which tells you the status of a nF import job. */
public static final String RESULT_STATUS_TAG = "ResultStatus";
/** The XML attribute which tells you the status of a nF import job. */
public static final String STATUS_ATTRIBUTE = "status";
/** The XML tag which holds the id of the nF job. */
public static final String JOB_ID = "jobId";
......@@ -32,6 +39,14 @@ public class ReportHandler extends DefaultHandler {
/** Scanned string will be stored here temporarily. */
private String currentString;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
if (qName.equalsIgnoreCase(RESULT_STATUS_TAG)) {
statusId = Integer.valueOf(attributes.getValue(STATUS_ATTRIBUTE));
}
}
/**
* If a tag has been read, its contents will be tested here. If it contains either a status id, job id or
* service exception messege, then the contents will be stored in the appropriate member variable.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment