Commit 0f1c41b0 authored by duminil's avatar duminil
Browse files

novaFactory : Saving file to TmpFolder

parent 6eac4c9f
...@@ -4,71 +4,72 @@ ...@@ -4,71 +4,72 @@
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import eu.simstadt.nf4j.Connector;
import eu.simstadt.nf4j.ExportJob; import eu.simstadt.nf4j.ExportJob;
import eu.simstadt.nf4j.FailedTransmissionException; import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException; import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus; import eu.simstadt.nf4j.JobStatus;
import eu.simstadt.nf4j.Connector;
/** /**
* Export jobs are requests for CityGML models. Every valid export job has an id and a status. This implementation * Export jobs are requests for CityGML models. Every valid export job has an id and a status. This implementation
* offers non-blocking asynchronous send, poll and download operations, so that your main application has not to * offers non-blocking asynchronous send, poll and download operations, so that your main application has not to wait
* wait for the results. You may want to register your main application as a job status listeners at this job to * for the results. You may want to register your main application as a job status listeners at this job to get status
* get status updates from the asynchronous operations. * updates from the asynchronous operations.
* *
* @author Marcel Bruse * @author Marcel Bruse
*/ */
public class AsyncExportJob extends ExportJob<ExportJobDescription> implements AsyncJob { public class AsyncExportJob extends ExportJob<ExportJobDescription> implements AsyncJob
{
/** /**
* While polling for the current job status, the polling thread will sleep for this amount of seconds * While polling for the current job status, the polling thread will sleep for this amount of seconds before each
* before each status update request. * status update request.
*/ */
private final int DEFAULT_POLLING_INTERVAL = 5; // seconds private final int DEFAULT_POLLING_INTERVAL = 5; // seconds
/** There can only be one sending thread for each job at a time. */ /** There can only be one sending thread for each job at a time. */
private Thread sendThread; private Thread sendThread;
/** There can only be one polling thread for each job at a time. */ /** There can only be one polling thread for each job at a time. */
private Thread pollThread; private Thread pollThread;
/** There can only be one download thread for each job at a time. */ /** There can only be one download thread for each job at a time. */
private Thread downloadThread; private Thread downloadThread;
/** /**
* Once the send() operation has been triggered, this member will be true. No subsequent invocations of * Once the send() operation has been triggered, this member will be true. No subsequent invocations of send() will
* send() will be possible then. * be possible then.
*/ */
private boolean jobTransmissionTriggered = false; private boolean jobTransmissionTriggered = false;
/** As long as this variable is true, the polling thread will be kept alive. */ /** As long as this variable is true, the polling thread will be kept alive. */
private boolean keepPolling = true; private boolean keepPolling = true;
/** /**
* List of all registered job status listeners. Whenever the state of this job changes, these listeners * List of all registered job status listeners. Whenever the state of this job changes, these listeners will get
* will get informed. * informed.
*/ */
private LinkedList<JobStatusListener> jobListenerList = new LinkedList<>(); private LinkedList<JobStatusListener> jobListenerList = new LinkedList<>();
/** /**
* This job will be send and observed asynchronously. It's results will be downloaded asynchronously also. * This job will be send and observed asynchronously. Its results will be downloaded asynchronously also. If an
* If an asynchronous operation breaks, then the last encountered problem will be described here. * asynchronous operation breaks, then the last encountered problem will be described here.
*/ */
private Optional<String> lastEncounteredProblem = Optional.empty(); private Optional<String> lastEncounteredProblem = Optional.empty();
/** /**
* The last job status which has been sent to all registered job status listeners. * The last job status which has been sent to all registered job status listeners.
*/ */
private JobStatus lastPublishedJobStatus; private JobStatus lastPublishedJobStatus;
/** Once the CityGML file has been download, it should be referenced here. */ /** Once the CityGML file has been download, it should be referenced here. */
private File result; private File result;
/** /**
* This constructor forces the job to have a description and a connector instance. Every job which * This constructor forces the job to have a description and a connector instance. Every job which is created by this
* is created by this constructor will have the status "local", because it is assumed that it has an unsent * constructor will have the status "local", because it is assumed that it has an unsent description and no job id
* description and no job id yet. * yet.
* *
* @param connector The job will use this connector to synchronize itself with the nF. * @param connector The job will use this connector to synchronize itself with the nF.
* @param descriptor The description of this job. * @param descriptor The description of this job.
...@@ -80,21 +81,21 @@ public AsyncExportJob(ExportJobDescription descriptor, Connector<AsyncImportJob, ...@@ -80,21 +81,21 @@ public AsyncExportJob(ExportJobDescription descriptor, Connector<AsyncImportJob,
/** /**
* This constructor forces the job to have a id and a connector instance. Every job which is created by this * This constructor forces the job to have a id and a connector instance. Every job which is created by this
* constructor will have the status "sent", because it is assumed that the job is already enqueued at the * constructor will have the status "sent", because it is assumed that the job is already enqueued at the nF job
* nF job queue. * queue.
* *
* @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status * @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status will be
* will be updated. If you call updateStatus() and the job id is "unkown" on the server side, then * updated. If you call updateStatus() and the job id is "unkown" on the server side, then
* @param connector The job will use this connector to synchronize itself with the nF. * @param connector The job will use this connector to synchronize itself with the nF.
*/ */
public AsyncExportJob(int id, Connector<AsyncImportJob, AsyncExportJob> connector) { public AsyncExportJob(int id, Connector<AsyncImportJob, AsyncExportJob> connector) {
super(id, connector); super(id, connector);
status = JobStatus.SENT; status = JobStatus.SENT;
} }
/** /**
* Builds an XML job file from the job description and sends it to the nF server which has been configured * Builds an XML job file from the job description and sends it to the nF server which has been configured in your
* in your connector instance. This will be done asynchronously within a SendExportJobTask. * connector instance. This will be done asynchronously within a SendExportJobTask.
* *
* @throws FailedTransmissionException If this method has been called before, then you will receive this. * @throws FailedTransmissionException If this method has been called before, then you will receive this.
* @throws InvalidJobDescriptorException If the job description is invalid or null, then you will receive this. * @throws InvalidJobDescriptorException If the job description is invalid or null, then you will receive this.
...@@ -112,19 +113,18 @@ public synchronized void send() throws FailedTransmissionException, InvalidJobDe ...@@ -112,19 +113,18 @@ public synchronized void send() throws FailedTransmissionException, InvalidJobDe
sendThread = new Thread(new SendExportJobTask(this)); sendThread = new Thread(new SendExportJobTask(this));
sendThread.start(); sendThread.start();
} }
/** /**
* Frequently queries the status of the remote nF export job and updates the local status accordingly. * Frequently queries the status of the remote nF export job and updates the local status accordingly. The queries
* The queries will be performed asynchronously in a separate thread. Job status listeners will be notified * will be performed asynchronously in a separate thread. Job status listeners will be notified upon every new status
* upon every new status change. * change.
* *
* Note, there can only be one polling thread at a time. Subsequent calls of poll() will stop the previously * Note, there can only be one polling thread at a time. Subsequent calls of poll() will stop the previously started
* started poll threads. * poll threads.
* *
* @param interval Amount of seconds to wait before the next status update request will be sent to the * @param interval Amount of seconds to wait before the next status update request will be sent to the nF server.
* nF server.
* *
* @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this. * @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this.
*/ */
@Override @Override
public synchronized void poll(int interval) throws FailedTransmissionException { public synchronized void poll(int interval) throws FailedTransmissionException {
...@@ -138,33 +138,32 @@ public synchronized void poll(int interval) throws FailedTransmissionException { ...@@ -138,33 +138,32 @@ public synchronized void poll(int interval) throws FailedTransmissionException {
pollThread = new Thread(new PollJobStatusTask(this, interval)); pollThread = new Thread(new PollJobStatusTask(this, interval));
pollThread.start(); pollThread.start();
} }
/** /**
* Convenience method for polling with a predefined default interval. * Convenience method for polling with a predefined default interval.
* *
* Frequently queries the status of the remote nF export job and updates the local status accordingly. * Frequently queries the status of the remote nF export job and updates the local status accordingly. The queries
* The queries will be performed asynchronously in a separate thread. Job status listeners will be notified * will be performed asynchronously in a separate thread. Job status listeners will be notified upon every new status
* upon every new status change. * change.
* *
* Note, there can only be one polling thread at a time. Subsequent calls of poll() will stop the previously * Note, there can only be one polling thread at a time. Subsequent calls of poll() will stop the previously started
* started poll threads. * poll threads.
* *
* @param interval Amount of seconds to wait before the next status update request will be sent to the * @param interval Amount of seconds to wait before the next status update request will be sent to the nF server.
* nF server. * @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this.
* @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this.
* @see poll(int) * @see poll(int)
*/ */
public synchronized void poll() throws FailedTransmissionException { public synchronized void poll() throws FailedTransmissionException {
poll(DEFAULT_POLLING_INTERVAL); poll(DEFAULT_POLLING_INTERVAL);
} }
/** /**
* Connects to the nF and refreshes the status of this job. If there is no nF connector set, * Connects to the nF and refreshes the status of this job. If there is no nF connector set, this operation will
* this operation will throw a FailedTransmissionException. * throw a FailedTransmissionException.
* *
* @throws FailedTransmissionException You will receive this exception if no connector is present, the connection * @throws FailedTransmissionException You will receive this exception if no connector is present, the connection to
* to the nF is broken, the job has not been sent to the nF yet, or another update request is ongoing. In the two * the nF is broken, the job has not been sent to the nF yet, or another update request is ongoing. In the
* latter cases, job will either have the status "LOCAL" or "WAITING". * two latter cases, job will either have the status "LOCAL" or "WAITING".
*/ */
@Override @Override
public synchronized void updateStatus() throws FailedTransmissionException { public synchronized void updateStatus() throws FailedTransmissionException {
...@@ -180,10 +179,10 @@ public synchronized void updateStatus() throws FailedTransmissionException { ...@@ -180,10 +179,10 @@ public synchronized void updateStatus() throws FailedTransmissionException {
setStatus(job.getStatus(), Optional.empty()); setStatus(job.getStatus(), Optional.empty());
} }
} }
/** /**
* Calls updateStatus() for you, since updateStatus() is a protected method. This method is used by the * Calls updateStatus() for you, since updateStatus() is a protected method. This method is used by the asynchronous
* asynchronous PollJobStatusTask class. * PollJobStatusTask class.
*/ */
@Override @Override
public void triggerStatusUpdate() throws FailedTransmissionException { public void triggerStatusUpdate() throws FailedTransmissionException {
...@@ -191,57 +190,60 @@ public void triggerStatusUpdate() throws FailedTransmissionException { ...@@ -191,57 +190,60 @@ public void triggerStatusUpdate() throws FailedTransmissionException {
} }
/** /**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent * Sets the status of this job depending on the given nF status code. nF status codes will be sent to you in http
* to you in http responses. * responses.
* *
* @param statusCode The nF status code for this job. * @param statusCode The nF status code for this job.
*/ */
@Override @Override
public synchronized void setStatusForCode(int statusCode) { public synchronized void setStatusForCode(int statusCode) {
switch (statusCode) { switch (statusCode) {
case 0: case 0:
setStatus(JobStatus.PENDING); break; setStatus(JobStatus.PENDING);
break;
case 10: case 10:
setStatus(JobStatus.RUNNING); break; setStatus(JobStatus.RUNNING);
break;
case 20: case 20:
setStatus(JobStatus.FAILED); break; setStatus(JobStatus.FAILED);
break;
case 30: case 30:
setStatus(JobStatus.FINISHED); break; setStatus(JobStatus.FINISHED);
break;
default: default:
setStatus(JobStatus.UNKNOWN); setStatus(JobStatus.UNKNOWN);
} }
} }
/** /**
* @return Returns true, if the job is definitely done. This is also the case, if the resulting CityGML * @return Returns true, if the job is definitely done. This is also the case, if the resulting CityGML file has been
* file has been download. False, otherwise. * download. False, otherwise.
*/ */
@Override @Override
public boolean hasFinished() { public boolean hasFinished() {
return status == JobStatus.FINISHED || status == JobStatus.DOWNLOAD; return status == JobStatus.FINISHED || status == JobStatus.DOWNLOAD;
} }
/** /**
* @return Returns true, if the job has been failed. You may want to look up the "last encountered problem" * @return Returns true, if the job has been failed. You may want to look up the "last encountered problem" string.
* string.
*/ */
@Override @Override
public boolean hasFailed() { public boolean hasFailed() {
return status == JobStatus.FAILED; return status == JobStatus.FAILED;
} }
/** /**
* Registers a job status listener. * Registers a job status listener.
* *
* @param jobListener The job status listener to be registered. This listener will receive updates about every * @param jobListener The job status listener to be registered. This listener will receive updates about every
* progressing change of the job status. Meaning, the change to a particular status will only be signaled once * progressing change of the job status. Meaning, the change to a particular status will only be signaled
* to the listener. * once to the listener.
*/ */
@Override @Override
public void addJobStatusListener(JobStatusListener jobListener) { public void addJobStatusListener(JobStatusListener jobListener) {
jobListenerList.add(jobListener); jobListenerList.add(jobListener);
} }
/** /**
* Unregisters a job status listener. * Unregisters a job status listener.
* *
...@@ -251,12 +253,11 @@ public void addJobStatusListener(JobStatusListener jobListener) { ...@@ -251,12 +253,11 @@ public void addJobStatusListener(JobStatusListener jobListener) {
public void removeJobStatusListener(JobStatusListener jobListener) { public void removeJobStatusListener(JobStatusListener jobListener) {
jobListenerList.remove(jobListener); jobListenerList.remove(jobListener);
} }
/** /**
* Once the status of this job changes, all registered job status listeners will be notified. * Once the status of this job changes, all registered job status listeners will be notified. Listeners will only be
* Listeners will only be notified of the status updates where the status of the job progresses and they will * notified of the status updates where the status of the job progresses and they will only be notified once about
* only be notified once about every singular status. If this status has been signaled already, then the * every singular status. If this status has been signaled already, then the listeners will not be notified again.
* listeners will not be notified again.
*/ */
@Override @Override
public synchronized void notifyJobStatusListeners() { public synchronized void notifyJobStatusListeners() {
...@@ -268,7 +269,7 @@ public synchronized void notifyJobStatusListeners() { ...@@ -268,7 +269,7 @@ public synchronized void notifyJobStatusListeners() {
lastPublishedJobStatus = status; lastPublishedJobStatus = status;
} }
} }
/** /**
* Cancels all ongoing send, poll and download operations as soon as possible. * Cancels all ongoing send, poll and download operations as soon as possible.
*/ */
...@@ -285,7 +286,7 @@ public void cancel() { ...@@ -285,7 +286,7 @@ public void cancel() {
downloadThread.interrupt(); downloadThread.interrupt();
} }
} }
/** /**
* @return Returns true, if the polling thread should go on with its polling job. Otherwise, false. * @return Returns true, if the polling thread should go on with its polling job. Otherwise, false.
*/ */
...@@ -293,14 +294,13 @@ public void cancel() { ...@@ -293,14 +294,13 @@ public void cancel() {
public boolean keepPolling() { public boolean keepPolling() {
return keepPolling; return keepPolling;
} }
/** /**
* Starts downloading the export job result, if there is any. As soon as the download has been finished, * Starts downloading the export job result, if there is any. As soon as the download has been finished, the job
* the job status will be set to DOWNLOADED. All registered job status listeners will get notified about * status will be set to DOWNLOADED. All registered job status listeners will get notified about the it. Afterwards,
* the it. Afterwards, you may want to obtain a handle to the download CityGML file with getResult(). * you may want to obtain a handle to the download CityGML file with getResult().
* *
* @throws FailedTransmissionException If the job has not been finished yet, then you will get some * @throws FailedTransmissionException If the job has not been finished yet, then you will get some of this.
* of this.
*/ */
public void downloadResult() throws FailedTransmissionException { public void downloadResult() throws FailedTransmissionException {
if (!hasFinished()) { if (!hasFinished()) {
...@@ -309,50 +309,48 @@ public void downloadResult() throws FailedTransmissionException { ...@@ -309,50 +309,48 @@ public void downloadResult() throws FailedTransmissionException {
downloadThread = new Thread(new DownloadTask(this)); downloadThread = new Thread(new DownloadTask(this));
downloadThread.start(); downloadThread.start();
} }
/** /**
* This method is used by the download task to set the CityGML file handle as soon as the file has * This method is used by the download task to set the CityGML file handle as soon as the file has been download.
* been download.
* *
* @param result The file handle of the download CityGML file. * @param result The file handle of the download CityGML file.
*/ */
protected void setResult(File result) { protected void setResult(File result) {
this.result = result; this.result = result;
} }
/** /**
* This method will return the download CityGML file for this export job, but only if the export job has * This method will return the download CityGML file for this export job, but only if the export job has actually
* actually been finished before. * been finished before.
* *
* @return Returns a file handle to the download CityGML file. * @return Returns a file handle to the download CityGML file.
* *
* @throws FailedTransmissionException If the job result has not been download yet, then you will get some * @throws FailedTransmissionException If the job result has not been download yet, then you will get some of this.
* of this.
*/ */
@Override @Override
public File getResult() throws FailedTransmissionException { public File getResult() throws FailedTransmissionException {
if (!hasFinished()) { if (!hasFinished()) {
throw new FailedTransmissionException("Job has not been finished!"); throw new FailedTransmissionException("Job has not been finished!");
} }
if (Objects.isNull(result)) { if (Objects.isNull(result)) {
throw new FailedTransmissionException("Job result has not been downloaded!"); throw new FailedTransmissionException("Job result has not been downloaded!");
} }
return result; return result;
} }
/** /**
* The asynchronous send, poll and download tasks cannot throw exceptions. If something goes wrong during the save, * The asynchronous send, poll and download tasks cannot throw exceptions. If something goes wrong during the save,
* poll or download operation, then you may want to submit at lease a textual description of the problem here. This * poll or download operation, then you may want to submit at lease a textual description of the problem here. This
* message will be sent to all registered job status listeners on the next status update. This is why you can use * message will be sent to all registered job status listeners on the next status update. This is why you can use the
* the convenience method setStatus(jobStatus, message), to do both at the same time. * convenience method setStatus(jobStatus, message), to do both at the same time.
* *
* @param errorMessage The description of the encountered problem. This could be the exception message or a more user * @param errorMessage The description of the encountered problem. This could be the exception message or a more user
* friendly message. * friendly message.
*/ */
protected synchronized void setLastEncounteredProblem(Optional<String> errorMessage) { protected synchronized void setLastEncounteredProblem(Optional<String> errorMessage) {
this.lastEncounteredProblem = errorMessage; this.lastEncounteredProblem = errorMessage;
} }
/** /**
* A convenience method to set a new job status and a status message at the same time. Status messages will be passed * A convenience method to set a new job status and a status message at the same time. Status messages will be passed
* by asynchronous tasks instead of exception, because they cannot throw exceptions. * by asynchronous tasks instead of exception, because they cannot throw exceptions.
...@@ -365,5 +363,5 @@ protected synchronized void setStatus(JobStatus jobStatus, Optional<String> mess ...@@ -365,5 +363,5 @@ protected synchronized void setStatus(JobStatus jobStatus, Optional<String> mess
lastEncounteredProblem = message; lastEncounteredProblem = message;
notifyJobStatusListeners(); notifyJobStatusListeners();
} }
} }
\ No newline at end of file
...@@ -19,86 +19,85 @@ ...@@ -19,86 +19,85 @@
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import eu.simstadt.nf4j.Connector;
import eu.simstadt.nf4j.Job;
import eu.simstadt.nf4j.FailedTransmissionException; import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException; import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.Job;
import eu.simstadt.nf4j.JobStatus; import eu.simstadt.nf4j.JobStatus;
import eu.simstadt.nf4j.Connector;
/** /**
* NFConnector lets you communicate with your novaFACTORY (nF) server instance. It supports nF version 6.3.1.1. * NFConnector lets you communicate with your novaFACTORY (nF) server instance. It supports nF version 6.3.1.1. For more
* For more technical details about the NFConnector interface @see NFConnector. * technical details about the NFConnector interface @see NFConnector.
* *
* Please note, that this connector doesn't act asynchronously. This connector is safe, but will block your main * Please note, that this connector doesn't act asynchronously. This connector is safe, but will block your main
* application. You may rather want to use the asynchronous job implementations. * application. You may rather want to use the asynchronous job implementations.
* *
* @author Marcel Bruse * @author Marcel Bruse
* *
* @param <I> The import job descriptor implementation for this connector. * @param <I> The import job descriptor implementation for this connector.
* @param <E> The export job descriptor implementation for this connector. * @param <E> The export job descriptor implementation for this connector.
*/ */
public class HTTPConnection implements Connector<AsyncImportJob, AsyncExportJob> { public class HTTPConnection implements Connector<AsyncImportJob, AsyncExportJob>
{
/** Supported version of the novaFACTORY. */ /** Supported version of the novaFACTORY. */
public static final String NOVA_FACTORY_VERSION = "6.3.1.1"; public static final String NOVA_FACTORY_VERSION = "6.3.1.1";
/** The default context of the nF web application. It is part of any request URL directed at the nF server. */ /** The default context of the nF web application. It is part of any request URL directed at the nF server. */
public static final String DEFAULT_CONTEXT = "novaFACTORY"; public static final String DEFAULT_CONTEXT = "novaFACTORY";
/** The default port number of the nF web application. */ /** The default port number of the nF web application. */
public static final int DEFAULT_PORT = 80; public static final int DEFAULT_PORT = 80;
/** The default protocol for requests. */ /** The default protocol for requests. */
public static final String DEFAULT_PROTOCOL = "http"; public static final String DEFAULT_PROTOCOL = "http";
/** The standard char set for requests. */ /** The standard char set for requests. */
public static final String DEFAULT_CHARSET = "UTF-8"; public static final String DEFAULT_CHARSET = "UTF-8";
/** The standard line separator required for file transmission via multipart/form-data. */ /** The standard line separator required for file transmission via multipart/form-data. */
public static final String CRLF = "\r\n"; public static final String CRLF = "\r\n";
/** The name of nF's remote order servlet. */ /** The name of nF's remote order servlet. */
public static final String REMOTE_ORDER_SERVLET = "RemoteOrder"; public static final String REMOTE_ORDER_SERVLET = "RemoteOrder";
/** The name of nF's remote status servlet. */ /** The name of nF's remote status servlet. */
public static final String REMOTE_STATUS_SERVLET = "RemoteStatus"; public static final String REMOTE_STATUS_SERVLET = "RemoteStatus";
/** The name of nF's remote import servlet. */ /** The name of nF's remote import servlet. */
public static final String REMOTE_IMPORT_SERVLET = "RemoteImport"; public static final String REMOTE_IMPORT_SERVLET = "RemoteImport";
/** Default parser error message. */ /** Default parser error message. */
public static final String XML_PARSER_ERROR = "Request failed due to an unknown XML parser error."; public static final String XML_PARSER_ERROR = "Request failed due to an unknown XML parser error.";
/** Default IO exception occurred message. */ /** Default IO exception occurred message. */
public static final String IO_EXCEPTION_OCCURRED = public static final String IO_EXCEPTION_OCCURRED =
"Request failed due to an IO exception. Check the host and port."; "Request failed due to an IO exception. Check the host and port.";
/** Default malformed URL message. */ /** Default malformed URL message. */
public static final String MALFORMED_URL = "Request failed due to a malformed URL."; public static final String MALFORMED_URL = "Request failed due to a malformed URL.";
/** Default unknown error message. */ /** Default unknown error message. */
public static final String UNKNOWN_ERROR_OCCURRED = "An unknown error occurred."; public static final String UNKNOWN_ERROR_OCCURRED = "An unknown error occurred.";
/** The server or host name of the nF server. */ /** The server or host name of the nF server. */
private String server; private String server;
/** The port of the nF server. */ /** The port of the nF server. */
private int port; private int port;
/** The protocol of the data connection. */ /** The protocol of the data connection. */
private String protocol; private String protocol;
/** The context of the nF web application. It is part of any request URL directed at the nF server. */ /** The context of the nF web application. It is part of any request URL directed at the nF server. */
private String context; private String context;
/** /**
* Constructs your Connector instance. * Constructs your Connector instance.
* *
...@@ -107,7 +106,7 @@ public class HTTPConnection implements Connector<AsyncImportJob, AsyncExportJob> ...@@ -107,7 +106,7 @@ public class HTTPConnection implements Connector<AsyncImportJob, AsyncExportJob>
public HTTPConnection(String server) { public HTTPConnection(String server) {
this(server, DEFAULT_PORT, DEFAULT_CONTEXT, DEFAULT_PROTOCOL); this(server, DEFAULT_PORT, DEFAULT_CONTEXT, DEFAULT_PROTOCOL);
} }
/** /**
* Constructs your Connector instance. * Constructs your Connector instance.
* *
...@@ -126,9 +125,9 @@ public HTTPConnection(String server, int port, String context, String protocol) ...@@ -126,9 +125,9 @@ public HTTPConnection(String server, int port, String context, String protocol)
* Callers of this NFConnector want to know the actual version of the novaFACTORY and the versions of its * Callers of this NFConnector want to know the actual version of the novaFACTORY and the versions of its
* HTTP/FTP/WPS/etc. interfaces against which this interface has been implemented. * HTTP/FTP/WPS/etc. interfaces against which this interface has been implemented.
* *
* The NovaFACTORY interfaces may change over time. Such changes force the SimStadt programmers to * The NovaFACTORY interfaces may change over time. Such changes force the SimStadt programmers to implement
* implement different versions of this NFConnector interface while keeping old implementations in order to * different versions of this NFConnector interface while keeping old implementations in order to ensure backward
* ensure backward compatibility. * compatibility.
* *
* @return The supported version of the novaFACTORY. * @return The supported version of the novaFACTORY.
*/ */
...@@ -136,7 +135,7 @@ public HTTPConnection(String server, int port, String context, String protocol) ...@@ -136,7 +135,7 @@ public HTTPConnection(String server, int port, String context, String protocol)
public String supportsNFVersion() { public String supportsNFVersion() {
return NOVA_FACTORY_VERSION; return NOVA_FACTORY_VERSION;
} }
/** /**
* Returns the status of any existing nF export job. * Returns the status of any existing nF export job.
* *
...@@ -162,7 +161,7 @@ public AsyncExportJob requestExportJob(int jobId) throws FailedTransmissionExcep ...@@ -162,7 +161,7 @@ public AsyncExportJob requestExportJob(int jobId) throws FailedTransmissionExcep
} }
return result; return result;
} }
/** /**
* Returns the status of any existing nF import job. * Returns the status of any existing nF import job.
* *
...@@ -190,12 +189,12 @@ public AsyncImportJob requestImportJob(int jobId) throws FailedTransmissionExcep ...@@ -190,12 +189,12 @@ public AsyncImportJob requestImportJob(int jobId) throws FailedTransmissionExcep
} }
return result; return result;
} }
/** /**
* Downloads the result for a given nF export job and hands over the corresponding file handle. * Downloads the result for a given nF export job and hands over the corresponding file handle.
* *
* @param jobId The id of the export job for which the result should be loaded. * @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. * @return A file handle to the result of the nF export job.
*/ */
@Override @Override
public File requestExportJobResult(AsyncExportJob job) throws FailedTransmissionException { public File requestExportJobResult(AsyncExportJob job) throws FailedTransmissionException {
...@@ -216,7 +215,7 @@ public File requestExportJobResult(AsyncExportJob job) throws FailedTransmission ...@@ -216,7 +215,7 @@ public File requestExportJobResult(AsyncExportJob job) throws FailedTransmission
} }
return result; return result;
} }
/** /**
* Builds a simple parameter string for a HTTP GET request string. * Builds a simple parameter string for a HTTP GET request string.
* *
...@@ -227,10 +226,9 @@ public File requestExportJobResult(AsyncExportJob job) throws FailedTransmission ...@@ -227,10 +226,9 @@ public File requestExportJobResult(AsyncExportJob job) throws FailedTransmission
private String buildParameter(String key, Object value) { private String buildParameter(String key, Object value) {
return key + "=" + value; return key + "=" + value;
} }
/** /**
* Builds a HTTP GET request URL out of the used protocol, nF server, port, context, servlet and existing * Builds a HTTP GET request URL out of the used protocol, nF server, port, context, servlet and existing parameters.
* parameters.
* *
* @param servlet One of the supported nF servlets RemoteOrder, RemoteStatus or RemoteImport. * @param servlet One of the supported nF servlets RemoteOrder, RemoteStatus or RemoteImport.
* @param parameters List of parameters to be send to the servlet. * @param parameters List of parameters to be send to the servlet.
...@@ -244,31 +242,31 @@ private URL buildURL(String servlet, List<String> parameters) throws MalformedUR ...@@ -244,31 +242,31 @@ private URL buildURL(String servlet, List<String> parameters) throws MalformedUR
} }
return new URL(url); return new URL(url);
} }
/** /**
* Requests a response from the given URL. The response is expected to be returned as nF XML report. * Requests a response from the given URL. The response is expected to be returned as nF XML report.
* *
* @param url The URL of the nF servlet with all its necessary parameters. Read the nF handbook for more * @param url The URL of the nF servlet with all its necessary parameters. Read the nF handbook for more details
* details about the usage of nF servlets. * about the usage of nF servlets.
* @return The response is expected to be a XML report, which will then be returned as a String. * @return The response is expected to be a XML report, which will then be returned as a String.
* @throws IOException You will get some of this, if anything goes wrong with your data connection to your * @throws IOException You will get some of this, if anything goes wrong with your data connection to your nF
* nF instance. * instance.
*/ */
private String getResponse(URL url) throws UnsupportedEncodingException, IOException { private String getResponse(URL url) throws UnsupportedEncodingException, IOException {
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setRequestProperty("Accept-Charset", DEFAULT_CHARSET); httpConnection.setRequestProperty("Accept-Charset", DEFAULT_CHARSET);
return getResponse(httpConnection); return getResponse(httpConnection);
} }
/** /**
* Requests a response from the given HTTP connection. The response is expected to be a XML document. * Requests a response from the given HTTP connection. The response is expected to be a XML document.
* *
* @param httpConnection The HTTP Connection to the nF servlet. Read the nF handbook for more details * @param httpConnection The HTTP Connection to the nF servlet. Read the nF handbook for more details about the usage
* about the usage of the nF servlets. * of the nF servlets.
* @return The response is expected to be a XML report, which will then be returned as a String. * @return The response is expected to be a XML report, which will then be returned as a String.
* @throws UnsupportedEncodingException You will get some of this, if your connection uses wrong encodings. * @throws UnsupportedEncodingException You will get some of this, if your connection uses wrong encodings.
* @throws IOException You will get some of this, if anything goes wrong with your data connection to your * @throws IOException You will get some of this, if anything goes wrong with your data connection to your nF
* nF instance. * instance.
*/ */
private String getResponse(HttpURLConnection httpConnection) throws UnsupportedEncodingException, IOException { private String getResponse(HttpURLConnection httpConnection) throws UnsupportedEncodingException, IOException {
String xml = ""; String xml = "";
...@@ -287,7 +285,7 @@ private String getResponse(HttpURLConnection httpConnection) throws UnsupportedE ...@@ -287,7 +285,7 @@ private String getResponse(HttpURLConnection httpConnection) throws UnsupportedE
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
xml += line; xml += line;
} }
} }
response.close(); response.close();
} else { } else {
...@@ -295,22 +293,22 @@ private String getResponse(HttpURLConnection httpConnection) throws UnsupportedE ...@@ -295,22 +293,22 @@ private String getResponse(HttpURLConnection httpConnection) throws UnsupportedE
} }
return xml; return xml;
} }
/** /**
* If everything works as expected, then nF's servlets will respond with XML reports to your requests. The job * If everything works as expected, then nF's servlets will respond with XML reports to your requests. The job id and
* id and status will be extracted from the response string. The id of the passed job instance will be updated. * status will be extracted from the response string. The id of the passed job instance will be updated. The XML
* The XML reports have a certain structure. Read the nF manual for more information about the XML reports and * reports have a certain structure. Read the nF manual for more information about the XML reports and have a look at
* have a look at the DTD of the XML reports. * the DTD of the XML reports.
* *
* @param xml A XML string that is supposed to be a XML document. * @param xml A XML string that is supposed to be a XML document.
* @param An empty job status object about to be filled with the actual result. It will be UNKOWN if there was * @param An empty job status object about to be filled with the actual result. It will be UNKOWN if there was no XML
* no XML report in the response. * report in the response.
* @throws ParserConfigurationException Something went wrong. * @throws ParserConfigurationException Something went wrong.
* @throws SAXException Some parse error. * @throws SAXException Some parse error.
* @throws IOException Some parse error. * @throws IOException Some parse error.
*/ */
private void getJobFromResponse(Job job, String xml) private void getJobFromResponse(Job job, String xml)
throws ParserConfigurationException, SAXException, IOException, FailedTransmissionException { throws ParserConfigurationException, SAXException, IOException, FailedTransmissionException {
SAXParserFactory saxFactory = SAXParserFactory.newInstance(); SAXParserFactory saxFactory = SAXParserFactory.newInstance();
SAXParser parser = saxFactory.newSAXParser(); SAXParser parser = saxFactory.newSAXParser();
StringReader reader = new StringReader(xml); StringReader reader = new StringReader(xml);
...@@ -326,13 +324,13 @@ private void getJobFromResponse(Job job, String xml) ...@@ -326,13 +324,13 @@ private void getJobFromResponse(Job job, String xml)
throw new FailedTransmissionException(handler.serviceException); throw new FailedTransmissionException(handler.serviceException);
} }
} }
/** /**
* Downloads a (zipped) CityGML file from the nF server. The file will be the result of an export job. * 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. * @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. Will be null if no file has been * @return Returns a handle to the downloaded (zipped) CityGML file. Will be null if no file has been downloaded (due
* downloaded (due to errors). * to errors).
* @throws IOException Something went wrong with your data connection. Check your server and the port. * @throws IOException Something went wrong with your data connection. Check your server and the port.
*/ */
private File downloadFile(URL url) throws IOException { private File downloadFile(URL url) throws IOException {
...@@ -341,26 +339,26 @@ private File downloadFile(URL url) throws IOException { ...@@ -341,26 +339,26 @@ private File downloadFile(URL url) throws IOException {
httpConnection.setRequestProperty("Accept-Charset", DEFAULT_CHARSET); httpConnection.setRequestProperty("Accept-Charset", DEFAULT_CHARSET);
if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
String filename = "result.gml"; String filename = "result.gml";
String disposition = httpConnection.getHeaderField("Content-Disposition"); String disposition = httpConnection.getHeaderField("Content-Disposition");
if (disposition != null) { if (disposition != null) {
int index = disposition.indexOf("filename="); int index = disposition.indexOf("filename=");
if (index > -1) { if (index > -1) {
filename = disposition. filename = disposition.
substring(index + 9, disposition.length()). substring(index + 9, disposition.length()).
replaceAll("\"", ""). replaceAll("\"", "").
replaceAll(";", ""); replaceAll(";", "");
} }
} }
InputStream inputStream = httpConnection.getInputStream(); InputStream inputStream = httpConnection.getInputStream();
FileOutputStream outputStream = new FileOutputStream(filename); handle = Files.createTempDirectory("nfDownload").resolve(filename).toFile();
int bytesRead = -1; FileOutputStream outputStream = new FileOutputStream(handle);
byte[] buffer = new byte[4096]; int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) { byte[] buffer = new byte[4096];
outputStream.write(buffer, 0, bytesRead); while ((bytesRead = inputStream.read(buffer)) != -1) {
} outputStream.write(buffer, 0, bytesRead);
outputStream.close(); }
inputStream.close(); outputStream.close();
handle = new File(filename); inputStream.close();
} else { } else {
throw new IOException(); throw new IOException();
} }
...@@ -372,10 +370,10 @@ private File downloadFile(URL url) throws IOException { ...@@ -372,10 +370,10 @@ private File downloadFile(URL url) throws IOException {
* *
* @param file The XML file which describes the nF export job. * @param file The XML file which describes the nF export job.
* @return Returns the status of the export job you just sent. * @return Returns the status of the export job you just sent.
* @throws InvalidJobDescriptorException * @throws InvalidJobDescriptorException
*/ */
@Override @Override
public void sendAndUpdateExportJob(AsyncExportJob job) public void sendAndUpdateExportJob(AsyncExportJob job)
throws InvalidJobDescriptorException, FailedTransmissionException { throws InvalidJobDescriptorException, FailedTransmissionException {
JobFileBuilderImpl jobFileBuilder = new JobFileBuilderImpl(); JobFileBuilderImpl jobFileBuilder = new JobFileBuilderImpl();
File exportJobFile = jobFileBuilder.buildExportJobFile(job.getDescriptor()); File exportJobFile = jobFileBuilder.buildExportJobFile(job.getDescriptor());
...@@ -386,7 +384,7 @@ public void sendAndUpdateExportJob(AsyncExportJob job) ...@@ -386,7 +384,7 @@ public void sendAndUpdateExportJob(AsyncExportJob job)
connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setDoOutput(true); // Sends export job file connection.setDoOutput(true); // Sends export job file
connection.setDoInput(true); // Expects XML response connection.setDoInput(true); // Expects XML response
connection.connect(); connection.connect();
OutputStream os = connection.getOutputStream(); OutputStream os = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, DEFAULT_CHARSET), true); PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, DEFAULT_CHARSET), true);
...@@ -412,34 +410,30 @@ public void sendAndUpdateExportJob(AsyncExportJob job) ...@@ -412,34 +410,30 @@ public void sendAndUpdateExportJob(AsyncExportJob job)
} }
/** /**
* Sends an import job to nF. The given import job file has to be a zip file and has to obey the internal * Sends an import job to nF. The given import job file has to be a zip file and has to obey the internal structure
* structure defined in the nF manuals. Short description: * defined in the nF manuals. Short description:
* *
* Import jobs enable you to add, alter and delete CityGML top level objects (like buildings) in the nF. * Import jobs enable you to add, alter and delete CityGML top level objects (like buildings) in the nF. Every import
* Every import job file has to contain a start file. Start files control the import process through their * job file has to contain a start file. Start files control the import process through their file names and their
* file names and their contents. The name of a start file has the following structure * contents. The name of a start file has the following structure
* *
* <Product>_<Tile>.start * <Product>_<Tile>.start
* *
* The start file should contain the level to which your manipulated CityGML should be imported. Your CityGML * The start file should contain the level to which your manipulated CityGML should be imported. Your CityGML file
* file and the ZIP archive has to be named after the following naming convention: * and the ZIP archive has to be named after the following naming convention:
* *
* <Product>_<Tile>_<Level>_<Operation>.gml * <Product>_<Tile>_<Level>_<Operation>.gml
* *
* Here operation can be ... * Here operation can be ... - 'REP': Replaces whole existing buildings only, - 'REPUPD': Replaces whole existing
* - 'REP': Replaces whole existing buildings only, * buildings and adds new buildings, - 'UPD': Update, same as REP, - 'CHG': Change, same as REPUPD, - 'DEL': Deletes
* - 'REPUPD': Replaces whole existing buildings and adds new buildings, * the geometry of a particular LOD, - 'DELALL': Deletes a whole building
* - '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. * @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. * @return Returns the job status of your import job.
* @throws FailedTransmissionException * @throws FailedTransmissionException
*/ */
@Override @Override
public void sendAndUpdateImportJob(AsyncImportJob job) public void sendAndUpdateImportJob(AsyncImportJob job)
throws InvalidJobDescriptorException, FailedTransmissionException { throws InvalidJobDescriptorException, FailedTransmissionException {
if (job.getStatus() != JobStatus.LOCAL) { if (job.getStatus() != JobStatus.LOCAL) {
throw new FailedTransmissionException("Job cannot be sent twice!"); throw new FailedTransmissionException("Job cannot be sent twice!");
...@@ -449,9 +443,9 @@ public void sendAndUpdateImportJob(AsyncImportJob job) ...@@ -449,9 +443,9 @@ public void sendAndUpdateImportJob(AsyncImportJob job)
try { try {
String product = job.getDescriptor().getProduct(); String product = job.getDescriptor().getProduct();
List<String> parameters = Arrays.asList( List<String> parameters = Arrays.asList(
buildParameter("request", "imp"), // trigger import buildParameter("request", "imp"), // trigger import
buildParameter("pdctKrz", product), // the nF product to import to buildParameter("pdctKrz", product), // the nF product to import to
buildParameter("createjob", "1")); // force nF to create an import job with a job number buildParameter("createjob", "1")); // force nF to create an import job with a job number
URL url = buildURL(REMOTE_IMPORT_SERVLET, parameters); URL url = buildURL(REMOTE_IMPORT_SERVLET, parameters);
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST"); connection.setRequestMethod("POST");
...@@ -459,7 +453,7 @@ public void sendAndUpdateImportJob(AsyncImportJob job) ...@@ -459,7 +453,7 @@ public void sendAndUpdateImportJob(AsyncImportJob job)
connection.setRequestProperty("Accept", "*/*"); connection.setRequestProperty("Accept", "*/*");
connection.setRequestProperty("", ""); connection.setRequestProperty("", "");
connection.setDoOutput(true); // Sends export job file connection.setDoOutput(true); // Sends export job file
connection.setDoInput(true); // Expects XML response connection.setDoInput(true); // Expects XML response
connection.connect(); connection.connect();
OutputStream os = connection.getOutputStream(); OutputStream os = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, DEFAULT_CHARSET), true); PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, DEFAULT_CHARSET), true);
...@@ -483,5 +477,5 @@ public void sendAndUpdateImportJob(AsyncImportJob job) ...@@ -483,5 +477,5 @@ public void sendAndUpdateImportJob(AsyncImportJob job)
throw new FailedTransmissionException(ex.getMessage()); throw new FailedTransmissionException(ex.getMessage());
} }
} }
} }
\ No newline at end of file
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