diff --git a/src/eu/simstadt/nf4j/Connector.java b/src/eu/simstadt/nf4j/Connector.java index 826ee289dfef2fc90e7b83ad60e3a8b7ddc4d085..169e8f4ffc6f5fe0c6bc8521a06cea4cd2ff940f 100644 --- a/src/eu/simstadt/nf4j/Connector.java +++ b/src/eu/simstadt/nf4j/Connector.java @@ -48,7 +48,7 @@ public void sendAndUpdateExportJob(E exportJob) * <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: + * file and the ZIP archive to be sent has to be named after the following naming convention: * * <Product>_<Tile>_<Level>_<Operation>.gml * diff --git a/src/eu/simstadt/nf4j/Job.java b/src/eu/simstadt/nf4j/Job.java index ec9b53f9b013bc34c46815a8c28ab90a96bfcf4b..7938914f71e92fda2cf75b9c2643edbcbb51fce5 100644 --- a/src/eu/simstadt/nf4j/Job.java +++ b/src/eu/simstadt/nf4j/Job.java @@ -17,7 +17,8 @@ public abstract class Job { protected Connector<?, ?> connector; /** - * Every job has it's status. Look up the different possible values in the JobStatus enumeration. + * Every job has a status. This method returns it. Look up the different possible values in the JobStatus + * enumeration. * * @return Returns the status of this job. */ diff --git a/src/eu/simstadt/nf4j/async/AsyncExportJob.java b/src/eu/simstadt/nf4j/async/AsyncExportJob.java index 69d3a965b3d80c31852af98433a697ca82140734..63938096a508bbfc6fef9d7e5b37529c0abe7aa4 100644 --- a/src/eu/simstadt/nf4j/async/AsyncExportJob.java +++ b/src/eu/simstadt/nf4j/async/AsyncExportJob.java @@ -114,8 +114,8 @@ public synchronized void send() throws FailedTransmissionException, InvalidJobDe } /** - * Frequently queries the remote status of the nF export job and updates the local status accordingly. - * The queries will be performed asynchronously in a separate thread. Job status listener will be notified + * Frequently queries the status of the remote nF export job and updates the local status accordingly. + * The queries will be performed asynchronously in a separate thread. Job status listeners will be notified * upon every new status change. * * Note, there can only be one polling thread at a time. Subsequent calls of poll() will stop the previously @@ -142,6 +142,16 @@ public synchronized void poll(int interval) throws FailedTransmissionException { /** * 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. + * The queries will be performed asynchronously in a separate thread. Job status listeners will be notified + * upon every new status change. + * + * Note, there can only be one polling thread at a time. Subsequent calls of poll() will stop the previously + * started poll threads. + * + * @param interval Amount of seconds to wait before the next status update request will be sent to the + * nF server. + * @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this. * @see poll(int) */ public synchronized void poll() throws FailedTransmissionException { diff --git a/src/eu/simstadt/nf4j/async/AsyncImportJob.java b/src/eu/simstadt/nf4j/async/AsyncImportJob.java index f19d71c126713d331d730d76c41833bfeb122896..c7e7c6205efa6024ccbf8cec4fad5558441483e3 100644 --- a/src/eu/simstadt/nf4j/async/AsyncImportJob.java +++ b/src/eu/simstadt/nf4j/async/AsyncImportJob.java @@ -90,7 +90,7 @@ public synchronized void send() throws InvalidJobDescriptorException, FailedTran } /** - * Frequently queries the remote status of the 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 will be performed asynchronously in a separate thread. Job status listener will be notified * upon every new status change. * diff --git a/src/eu/simstadt/nf4j/async/AsyncJob.java b/src/eu/simstadt/nf4j/async/AsyncJob.java index 25a4fa173b037eb01befc1d3c70c2d045535cd43..03e7e8a0a2d4661ae9cbc10d82cd82cea810f4cd 100644 --- a/src/eu/simstadt/nf4j/async/AsyncJob.java +++ b/src/eu/simstadt/nf4j/async/AsyncJob.java @@ -13,7 +13,7 @@ public interface AsyncJob { /** - * Frequently queries the remote status of the 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 will be performed asynchronously in a separate thread. Job status listener will be notified * upon every new status change. * diff --git a/src/eu/simstadt/nf4j/async/HTTPConnection.java b/src/eu/simstadt/nf4j/async/HTTPConnection.java index 888bde0f704580325f0e63d07f80dd27b4d863de..6349899954d6ee03fb6932c56ad4abb2b60e1f1a 100644 --- a/src/eu/simstadt/nf4j/async/HTTPConnection.java +++ b/src/eu/simstadt/nf4j/async/HTTPConnection.java @@ -100,7 +100,7 @@ public class HTTPConnection implements Connector<AsyncImportJob, AsyncExportJob> private String context; /** - * Constructs your NFConnector instance. + * Constructs your Connector instance. * * @param server The host name of the nF server with which you want to establish a data connection. */ @@ -109,7 +109,7 @@ public HTTPConnection(String server) { } /** - * Constructs your NFConnector instance. + * Constructs your Connector instance. * * @param server The host name of the nF server with which you want to establish a data connection. * @param port The port of the nF web application. @@ -298,8 +298,9 @@ private String getResponse(HttpURLConnection httpConnection) throws UnsupportedE /** * If everything works as expected, then nF's servlets will respond with XML reports to your requests. The job - * status will be extracted from the response string. The XML reports have a certain structure. Read the nF - * manual for more information about the XML reports and have a look at the DTD of the XML reports. + * id and status will be extracted from the response string. The id of the passed job instance will be updated. + * The XML reports have a certain structure. Read the nF 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. * @param An empty job status object about to be filled with the actual result. It will be UNKOWN if there was @@ -421,7 +422,7 @@ public void sendAndUpdateExportJob(AsyncExportJob job) * <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: + * file and the ZIP archive has to be named after the following naming convention: * * <Product>_<Tile>_<Level>_<Operation>.gml * @@ -466,6 +467,11 @@ public void sendAndUpdateImportJob(AsyncImportJob job) os.flush(); writer.append(CRLF).flush(); getJobFromResponse(job, getResponse(connection)); + if (job.getId() > 0) { + job.setStatus(JobStatus.SENT, Optional.empty()); + } else { + throw new FailedTransmissionException("Job didn't receive an id from the nF server."); + } } catch (MalformedURLException ex) { job.getStatus().setMessage(MALFORMED_URL); throw new FailedTransmissionException(ex.getMessage()); diff --git a/src/eu/simstadt/nf4j/async/SendExportJobTask.java b/src/eu/simstadt/nf4j/async/SendExportJobTask.java index e46a1691381c6b64484d5a395ff1b9d1cc0ba0ff..b7495dc413869277da4a351474190673a27f08f4 100644 --- a/src/eu/simstadt/nf4j/async/SendExportJobTask.java +++ b/src/eu/simstadt/nf4j/async/SendExportJobTask.java @@ -36,7 +36,6 @@ public void run() { try { HTTPConnection connector = (HTTPConnection) job.getConnector(); connector.sendAndUpdateExportJob(job); - job.setStatus(JobStatus.SENT, Optional.empty()); job.poll(); } catch (InvalidJobDescriptorException ex) { signalError("Job cancel because of an invalid job description!"); @@ -46,10 +45,10 @@ public void run() { } /** - * This method is superfluous I guess? Please refactor it. + * This method is superfluous I guess? TODO: Please check and refactor it. */ private void signalError(String errorMessage) { job.setStatus(JobStatus.UNKOWN, Optional.of(errorMessage)); } -} +} \ No newline at end of file diff --git a/src/eu/simstadt/nf4j/async/SendImportJobTask.java b/src/eu/simstadt/nf4j/async/SendImportJobTask.java index b9eeef51557f2ea373c81b266931a309da25297e..07b3073055aa3674b06b46ab04cea4b4bfdfb48f 100644 --- a/src/eu/simstadt/nf4j/async/SendImportJobTask.java +++ b/src/eu/simstadt/nf4j/async/SendImportJobTask.java @@ -36,7 +36,6 @@ public void run() { try { HTTPConnection connector = (HTTPConnection) job.getConnector(); connector.sendAndUpdateImportJob(job); - job.setStatus(JobStatus.SENT, Optional.empty()); job.poll(); } catch (InvalidJobDescriptorException ex) { signalError("Job cancel because of an invalid job description!"); @@ -45,8 +44,11 @@ public void run() { } } + /** + * This method is superfluous I guess? TODO: Please check and refactor it. + */ private void signalError(String errorMessage) { job.setStatus(JobStatus.UNKOWN, Optional.of(errorMessage)); } -} +} \ No newline at end of file diff --git a/src/eu/simstadt/nf4j/async/Client.java b/test/eu/simstadt/nf4j/async/test/SuccessfulExportJob.java similarity index 50% rename from src/eu/simstadt/nf4j/async/Client.java rename to test/eu/simstadt/nf4j/async/test/SuccessfulExportJob.java index d90bf9505a9e76d982a94faf17e1bccd35bad48c..fd8a752750d9e22af8436132dd84c36853645817 100644 --- a/src/eu/simstadt/nf4j/async/Client.java +++ b/test/eu/simstadt/nf4j/async/test/SuccessfulExportJob.java @@ -1,82 +1,94 @@ -package eu.simstadt.nf4j.async; +package eu.simstadt.nf4j.async.test; import java.io.File; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; import eu.simstadt.nf4j.FailedTransmissionException; import eu.simstadt.nf4j.InvalidJobDescriptorException; import eu.simstadt.nf4j.JobStatus; +import eu.simstadt.nf4j.async.AsyncExportJob; +import eu.simstadt.nf4j.async.ExportJobDescription; +import eu.simstadt.nf4j.async.HTTPConnection; +import eu.simstadt.nf4j.async.JobStatusEvent; +import eu.simstadt.nf4j.async.JobStatusListener; +import eu.simstadt.nf4j.async.Layer; +import eu.simstadt.nf4j.async.Unit; /** - * This is a class for development and test purposes. It is not needed for production. You may want to delete it. - * + * This class contains client oriented export job tests. It will send an export job and listens to + * status updates. Every of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED and DOWNLOAD + * have to be signaled to this test class. + * * @author Marcel Bruse */ -public class Client implements JobStatusListener { +public class SuccessfulExportJob implements JobStatusListener { public AsyncExportJob job; - - public static void main(String[] args) { - Client client = new Client(); - client.processJob(); - } - - public void processJob() { - // Describe the export job + + @Test + public void processJob() throws InterruptedException { ExportJobDescription description= ExportJobDescription.getDefaultDescriptor(); description.setInitiator("2758"); description.setAccount("Bruse"); description.setProduct("WU3"); description.setJobnumber("Bruse_0815"); - // Set at least one unit. You could also define a region polygon. Unit unit = Unit.getDefaultUnit(); unit.setValue("821"); description.addUnit(unit); - // Set at least one layer Layer layer = Layer.getDefaultLayer(); layer.setProduct("WU3"); layer.setName("GML"); description.addLayer(layer); - // Create a new export job with the job description and a HTTP connection job = new AsyncExportJob(description, new HTTPConnection("193.196.136.164")); - - // Register a job status listener job.addJobStatusListener(this); try { - // Send the export job to the nF server in a separate thread job.send(); } catch (FailedTransmissionException ex) { - // There was a problem with your connection or the job has been sent twice ex.printStackTrace(); } catch (InvalidJobDescriptorException ex) { - // The export job description is invalid ex.printStackTrace(); } + + // Wait for timeout, failure or that all tests pass + long timeout = 1000 * 60 * 3l; // 3 minutes maximum + long interval = 10000l; + while (!job.hasFinished() && !job.hasFailed() && timeout > 0) { + Thread.sleep(interval); + timeout -= interval; + } } - // This client is a job status listener and will get notified upon job status changes @Override public void jobStatusChanged(JobStatusEvent event) { JobStatus status = (JobStatus) event.getSource(); System.out.println(status); - if (status == JobStatus.FINISHED) { + if (status == JobStatus.LOCAL) { + assertTrue(true); + } else if (status == JobStatus.SENT) { + assertTrue(true); + } else if (status == JobStatus.PENDING) { + assertTrue(true); + } else if (status == JobStatus.RUNNING) { + assertTrue(true); + } else if (status == JobStatus.FINISHED) { try { - // Start an asynchronous download of the resulting CityGML file + assertTrue(true); job.downloadResult(); } catch (FailedTransmissionException ex) { - // Only finished jobs can download files ex.printStackTrace(); } } else if (status == JobStatus.DOWNLOAD) { try { - // Obtain the CityGML file handle File file = job.getResult(); + assertTrue(file.canRead()); System.out.println("CityGML at " + file.getAbsolutePath()); } catch (FailedTransmissionException ex) { - // Download may have failed ex.printStackTrace(); } } diff --git a/test/eu/simstadt/nf4j/async/test/SuccessfulImportJob.java b/test/eu/simstadt/nf4j/async/test/SuccessfulImportJob.java new file mode 100644 index 0000000000000000000000000000000000000000..b263644366e5c7167a96bed42b7865c77cd4205e --- /dev/null +++ b/test/eu/simstadt/nf4j/async/test/SuccessfulImportJob.java @@ -0,0 +1,74 @@ +package eu.simstadt.nf4j.async.test; + +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Test; + +import eu.simstadt.nf4j.FailedTransmissionException; +import eu.simstadt.nf4j.InvalidJobDescriptorException; +import eu.simstadt.nf4j.JobStatus; +import eu.simstadt.nf4j.async.AsyncImportJob; +import eu.simstadt.nf4j.async.HTTPConnection; +import eu.simstadt.nf4j.async.ImportJobDescription; +import eu.simstadt.nf4j.async.JobStatusEvent; +import eu.simstadt.nf4j.async.JobStatusListener; + +/** + * This class contains client oriented import job tests. It will send an import job and listens to + * status updates. Every of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED + * have to be signaled to this test class. + * + * @author Marcel Bruse + */ +public class SuccessfulImportJob implements JobStatusListener { + + public AsyncImportJob job; + + @Test + public void processJob() throws InterruptedException { + ImportJobDescription desc = ImportJobDescription.getDefaultDescriptor(); + desc.setProduct("LBTEST"); + desc.setLeaf("GR"); + desc.setCityGMLFile(new File("SomeBuildings.gml")); + + HTTPConnection connector = new HTTPConnection("193.196.136.164"); + job = new AsyncImportJob(desc, connector); + job.addJobStatusListener(this); + + try { + job.send(); + } catch (InvalidJobDescriptorException ex) { + ex.printStackTrace(); + } catch (FailedTransmissionException ex) { + ex.printStackTrace(); + } + + // Wait for timeout, failure or that all tests pass + long timeout = 1000 * 60 * 5l; // 5 minutes maximum + long interval = 10000l; + while (!job.hasFinished() && !job.hasFailed() && timeout > 0) { + Thread.sleep(interval); + timeout -= interval; + } + } + + @Override + public void jobStatusChanged(JobStatusEvent event) { + JobStatus status = (JobStatus) event.getSource(); + System.out.println(status); + if (status == JobStatus.LOCAL) { + assertTrue(true); + } else if (status == JobStatus.SENT) { + assertTrue(true); + } else if (status == JobStatus.PENDING) { + assertTrue(true); + } else if (status == JobStatus.RUNNING) { + assertTrue(true); + } else if (status == JobStatus.FINISHED) { + assertTrue(true); + } + } + +} \ No newline at end of file