]> git.argeo.org Git - gpl/argeo-slc.git/commitdiff
Add SlcExecution notifiers
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 22 Apr 2008 21:04:03 +0000 (21:04 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 22 Apr 2008 21:04:03 +0000 (21:04 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@1029 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

12 files changed:
org.argeo.slc.core/src/main/java/org/argeo/slc/ant/AntRegistryUtil.java
org.argeo.slc.core/src/main/java/org/argeo/slc/ant/SlcExecutionBuildListener.java [new file with mode: 0644]
org.argeo.slc.core/src/main/java/org/argeo/slc/ant/SlcProjectHelper.java
org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/FileSlcExecutionNotifier.java [new file with mode: 0644]
org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/SlcExecution.java
org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/SlcExecutionNotifier.java [new file with mode: 0644]
org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/WebServiceSlcExecutionNotifier.java [new file with mode: 0644]
org.argeo.slc.core/src/main/java/org/argeo/slc/dao/process/SlcExecutionDao.java
org.argeo.slc.core/src/main/java/org/argeo/slc/hibernate/process/SlcExecutionDaoHibernate.java
org.argeo.slc.core/src/test/java/org/argeo/slc/ant/SlcAntTest.java [new file with mode: 0644]
org.argeo.slc.core/src/test/resources/log4j.properties
org.argeo.slc.core/src/test/resources/org/argeo/slc/ant/build.xml [new file with mode: 0644]

index 8bb0ffcb7efc67d1ea27692cd4fd00ec586d3699..f9928708a23e20086301f8a742e6a384813212d3 100644 (file)
@@ -8,6 +8,7 @@ import java.util.Properties;
 \r
 import org.apache.commons.logging.Log;\r
 import org.apache.commons.logging.LogFactory;\r
+import org.apache.tools.ant.BuildException;\r
 import org.apache.tools.ant.Project;\r
 import org.apache.tools.ant.ProjectHelper;\r
 \r
@@ -56,7 +57,8 @@ public class AntRegistryUtil {
                                .getReference(SlcProjectHelper.REF_STRUCTURE_REGISTRY);\r
                registry.setMode(StructureRegistry.ACTIVE);\r
                registry.setActivePaths(activePaths);\r
-               p.executeTarget(p.getDefaultTarget());\r
+\r
+               runProject(p, null);\r
                return p;\r
        }\r
 \r
@@ -71,7 +73,8 @@ public class AntRegistryUtil {
                ProjectHelper helper = new SlcProjectHelper();\r
                p.addReference(ProjectHelper.PROJECTHELPER_REFERENCE, helper);\r
                helper.parse(p, antFile);\r
-               p.executeTarget(target != null ? target : p.getDefaultTarget());\r
+\r
+               runProject(p, target);\r
                return p;\r
        }\r
 \r
@@ -81,15 +84,20 @@ public class AntRegistryUtil {
                        log.debug("Runs all paths of Ant URL " + url);\r
                Project p = new Project();\r
                p.setUserProperty("ant.file", url.toString());\r
-               //p.setBaseDir(url.toString());\r
+               // p.setBaseDir(url.toString());\r
                p.init();\r
                ProjectHelper helper = new SlcProjectHelper();\r
                p.addReference(ProjectHelper.PROJECTHELPER_REFERENCE, helper);\r
                helper.parse(p, url);\r
-               for(Map.Entry<Object, Object> entry : properties.entrySet()){\r
-                       p.setUserProperty(entry.getKey().toString(), entry.getValue().toString());\r
+\r
+               if (properties != null) {\r
+                       for (Map.Entry<Object, Object> entry : properties.entrySet()) {\r
+                               p.setUserProperty(entry.getKey().toString(), entry.getValue()\r
+                                               .toString());\r
+                       }\r
                }\r
-               p.executeTarget(target != null ? target : p.getDefaultTarget());\r
+\r
+               runProject(p, target);\r
                return p;\r
        }\r
 \r
@@ -97,4 +105,16 @@ public class AntRegistryUtil {
        public static Project runAll(File antFile) {\r
                return runAll(antFile, null);\r
        }\r
+\r
+       protected static void runProject(Project p, String target) {\r
+               p.fireBuildStarted();\r
+               Throwable exception = null;\r
+               try {\r
+                       p.executeTarget(target != null ? target : p.getDefaultTarget());\r
+               } catch (Throwable e) {\r
+                       exception = e;\r
+               } finally {\r
+                       p.fireBuildFinished(exception);\r
+               }\r
+       }\r
 }\r
diff --git a/org.argeo.slc.core/src/main/java/org/argeo/slc/ant/SlcExecutionBuildListener.java b/org.argeo.slc.core/src/main/java/org/argeo/slc/ant/SlcExecutionBuildListener.java
new file mode 100644 (file)
index 0000000..cff4c17
--- /dev/null
@@ -0,0 +1,208 @@
+package org.argeo.slc.ant;\r
+\r
+import java.net.InetAddress;\r
+import java.net.UnknownHostException;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.UUID;\r
+import java.util.Vector;\r
+\r
+import org.apache.log4j.AppenderSkeleton;\r
+import org.apache.log4j.LogManager;\r
+import org.apache.log4j.spi.LoggingEvent;\r
+import org.apache.tools.ant.BuildEvent;\r
+import org.apache.tools.ant.BuildListener;\r
+import org.apache.tools.ant.Project;\r
+\r
+import org.argeo.slc.core.process.SlcExecution;\r
+import org.argeo.slc.core.process.SlcExecutionNotifier;\r
+import org.argeo.slc.core.process.SlcExecutionStep;\r
+\r
+public class SlcExecutionBuildListener extends AppenderSkeleton implements\r
+               BuildListener {\r
+       public static final String ANT_TYPE = "org.apache.tools.ant";\r
+       public static final String SLC_ANT_TYPE = "org.argeo.slc.ant";\r
+\r
+       public static final String REF_SLC_EXECUTION = "slcExecution";\r
+\r
+       private Project project;\r
+       \r
+       // to avoid stack overflow when logging for log4j\r
+       private boolean isLogging = false;\r
+\r
+       private List<SlcExecutionNotifier> notifiers = new Vector<SlcExecutionNotifier>();\r
+\r
+       private Map<SlcExecution, SlcExecutionStep> currentStep = new HashMap<SlcExecution, SlcExecutionStep>();\r
+\r
+       public void buildStarted(BuildEvent event) {\r
+               // SlcExecution slcExecution = getSlcExecution(event);\r
+\r
+       }\r
+\r
+       public void buildFinished(BuildEvent event) {\r
+               SlcExecution slcExecution = getSlcExecution(event);\r
+               slcExecution.setStatus(SlcExecution.STATUS_FINISHED);\r
+\r
+               for (SlcExecutionNotifier notifier : notifiers) {\r
+                       notifier.updateExecution(slcExecution);\r
+               }\r
+       }\r
+\r
+       public void messageLogged(BuildEvent event) {\r
+               SlcExecution slcExecution = getSlcExecution(event);\r
+               if (slcExecution != null) {\r
+                       SlcExecutionStep step = currentStep.get(slcExecution);\r
+                       if (step == null) {\r
+                               step = new SlcExecutionStep("LOG", event.getMessage());\r
+                               notifyStep(slcExecution, step);\r
+                       } else {\r
+                               step.addLog(event.getMessage());\r
+                       }\r
+               } else {\r
+                       // TODO: log before initialization?\r
+               }\r
+       }\r
+\r
+       public void targetStarted(BuildEvent event) {\r
+               addLogStep(event, "Target " + event.getTarget().getName() + " started");\r
+       }\r
+\r
+       public void targetFinished(BuildEvent event) {\r
+               addLogStep(event, "Target " + event.getTarget().getName() + " finished");\r
+       }\r
+\r
+       public void taskStarted(BuildEvent event) {\r
+               SlcExecution slcExecution = getSlcExecution(event);\r
+               SlcExecutionStep currStep = currentStep.get(slcExecution);\r
+               if (currStep != null) {\r
+                       notifyStep(slcExecution, currStep);\r
+                       currentStep.remove(currStep);\r
+               }\r
+\r
+               SlcExecutionStep step = new SlcExecutionStep("LOG", "Task "\r
+                               + event.getTask().getTaskName() + " started");\r
+               currentStep.put(slcExecution, step);\r
+       }\r
+\r
+       public void taskFinished(BuildEvent event) {\r
+               SlcExecution slcExecution = getSlcExecution(event);\r
+               SlcExecutionStep step = currentStep.get(slcExecution);\r
+               if (step != null) {\r
+                       step.addLog("Task " + event.getTask().getTaskName() + " finished");\r
+\r
+                       slcExecution.getSteps().add(step);\r
+                       notifyStep(slcExecution, step);\r
+\r
+                       currentStep.remove(slcExecution);\r
+               }\r
+       }\r
+\r
+       public void setNotifiers(List<SlcExecutionNotifier> notifiers) {\r
+               this.notifiers = notifiers;\r
+       }\r
+\r
+       protected SlcExecution getSlcExecution(BuildEvent event) {\r
+               Project project = event.getProject();\r
+               SlcExecution slcExecution = (SlcExecution) project\r
+                               .getReference(REF_SLC_EXECUTION);\r
+               if (slcExecution == null) {\r
+                       // for log4j\r
+                       this.project = project;// FIXME\r
+                       if (!LogManager.getRootLogger().isAttached(this)) {\r
+                               LogManager.getRootLogger().addAppender(this);\r
+                       }\r
+                       \r
+                       slcExecution = new SlcExecution();\r
+                       slcExecution.setUuid(UUID.randomUUID().toString());\r
+                       try {\r
+                               slcExecution.setHost(InetAddress.getLocalHost().getHostName());\r
+                       } catch (UnknownHostException e) {\r
+                               slcExecution.setHost(SlcExecution.UNKOWN_HOST);\r
+                       }\r
+\r
+                       if (project.getReference(SlcProjectHelper.REF_ROOT_CONTEXT) != null) {\r
+                               slcExecution.setType(SLC_ANT_TYPE);\r
+                       } else {\r
+                               slcExecution.setType(ANT_TYPE);\r
+                       }\r
+\r
+                       slcExecution.setPath(project.getProperty("ant.file"));\r
+                       slcExecution.setStatus(SlcExecution.STATUS_RUNNING);\r
+\r
+                       project.addReference(REF_SLC_EXECUTION, slcExecution);\r
+\r
+                       for (SlcExecutionNotifier notifier : notifiers) {\r
+                               notifier.newExecution(slcExecution);\r
+                       }\r
+\r
+               }\r
+               return slcExecution;\r
+       }\r
+\r
+       protected void addLogStep(BuildEvent event, String msg) {\r
+               SlcExecutionStep step = new SlcExecutionStep("LOG", msg);\r
+               SlcExecution slcExecution = getSlcExecution(event);\r
+               slcExecution.getSteps().add(step);\r
+\r
+               notifyStep(slcExecution, step);\r
+       }\r
+\r
+       protected void notifyStep(SlcExecution slcExecution, SlcExecutionStep step) {\r
+               Vector<SlcExecutionStep> additionalSteps = new Vector<SlcExecutionStep>();\r
+               additionalSteps.add(step);\r
+               notifySteps(slcExecution, additionalSteps);\r
+       }\r
+\r
+       protected void notifySteps(SlcExecution slcExecution,\r
+                       List<SlcExecutionStep> additionalSteps) {\r
+               for (SlcExecutionNotifier notifier : notifiers) {\r
+                       notifier.addSteps(slcExecution, additionalSteps);\r
+               }\r
+       }\r
+\r
+       /* Log4j methods */\r
+\r
+       @Override\r
+       protected void append(LoggingEvent event) {\r
+               if(isLogging){\r
+                       // avoid StackOverflow if notification calls Log4j itself.\r
+                       return;\r
+               }\r
+               isLogging = true;\r
+               \r
+               try {\r
+                       SlcExecution slcExecution = (SlcExecution) project\r
+                                       .getReference(REF_SLC_EXECUTION);\r
+                       if (slcExecution != null) {\r
+                               SlcExecutionStep step = currentStep.get(slcExecution);\r
+                               if (step == null) {\r
+                                       step = new SlcExecutionStep("LOG", event.getMessage()\r
+                                                       .toString());\r
+                                       notifyStep(slcExecution, step);\r
+                               } else {\r
+                                       step.addLog(event.getMessage().toString());\r
+                               }\r
+                       } else {\r
+                               // TODO: log before initialization?\r
+                       }\r
+               } finally{\r
+                       isLogging = false;\r
+               }\r
+               \r
+               \r
+       }\r
+\r
+       @Override\r
+       public void close() {\r
+               // TODO Auto-generated method stub\r
+\r
+       }\r
+\r
+       @Override\r
+       public boolean requiresLayout() {\r
+               // TODO Auto-generated method stub\r
+               return false;\r
+       }\r
+\r
+}\r
index ab2cc176385bca9a387adbb13e0553654df6bad9..b20abd34f96f9bda52339a1d32a254e91416d274 100644 (file)
@@ -14,6 +14,7 @@ import org.springframework.context.support.FileSystemXmlApplicationContext;
 import org.apache.commons.logging.Log;\r
 import org.apache.commons.logging.LogFactory;\r
 import org.apache.tools.ant.BuildException;\r
+import org.apache.tools.ant.BuildListener;\r
 import org.apache.tools.ant.Project;\r
 import org.apache.tools.ant.helper.ProjectHelper2;\r
 \r
@@ -176,6 +177,13 @@ public class SlcProjectHelper extends ProjectHelper2 {
                                '/' + acPath);\r
                context.registerShutdownHook();\r
                project.addReference(REF_ROOT_CONTEXT, context);\r
+\r
+               // Add build listeners declared in Spring context\r
+               Map<String, BuildListener> listeners = context.getBeansOfType(\r
+                               BuildListener.class, false, true);\r
+               for(BuildListener listener: listeners.values()){\r
+                       project.addBuildListener(listener);\r
+               }\r
        }\r
 \r
        /** Loads the SLC specific Ant tasks. */\r
diff --git a/org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/FileSlcExecutionNotifier.java b/org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/FileSlcExecutionNotifier.java
new file mode 100644 (file)
index 0000000..1bf4156
--- /dev/null
@@ -0,0 +1,80 @@
+package org.argeo.slc.core.process;\r
+\r
+import java.io.File;\r
+import java.io.FileWriter;\r
+import java.text.SimpleDateFormat;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import javax.xml.transform.stream.StreamResult;\r
+\r
+import org.springframework.oxm.Marshaller;\r
+\r
+import org.apache.commons.io.IOUtils;\r
+\r
+import org.argeo.slc.core.SlcException;\r
+\r
+public class FileSlcExecutionNotifier implements SlcExecutionNotifier {\r
+       private final static SimpleDateFormat sdf = new SimpleDateFormat(\r
+                       "yyyyMMdd-HHmmss");\r
+\r
+       private String basePath;\r
+       private Marshaller marshaller;\r
+\r
+       private Map<String, String> uuidToDir = new HashMap<String, String>();\r
+\r
+       public void addSteps(SlcExecution slcExecution,\r
+                       List<SlcExecutionStep> additionalSteps) {\r
+               writeSlcExecution(slcExecution);\r
+       }\r
+\r
+       public void newExecution(SlcExecution slcExecution) {\r
+               String dirPath = basePath + File.separator + sdf.format(new Date())\r
+                               + '-' + slcExecution.getUuid();\r
+               File dir = new File(dirPath);\r
+               dir.mkdirs();\r
+\r
+               uuidToDir.put(slcExecution.getUuid(), dirPath);\r
+\r
+               writeSlcExecution(slcExecution);\r
+       }\r
+\r
+       public void updateExecution(SlcExecution slcExecution) {\r
+               writeSlcExecution(slcExecution);\r
+       }\r
+\r
+       protected void writeSlcExecution(SlcExecution slcExecution) {\r
+               FileWriter out = null;\r
+               try {\r
+                       out = new FileWriter(getFilePath(slcExecution));\r
+                       marshaller.marshal(slcExecution, new StreamResult(out));\r
+               } catch (Exception e) {\r
+                       throw new SlcException("Cannot marshall SlcExecution to "\r
+                                       + getFilePath(slcExecution), e);\r
+               } finally {\r
+                       IOUtils.closeQuietly(out);\r
+               }\r
+       }\r
+\r
+       protected String getFileName(SlcExecution slcExecution) {\r
+               return "SlcExecution-" + slcExecution.getUuid() + ".xml";\r
+       }\r
+\r
+       protected String getFilePath(SlcExecution slcExecution) {\r
+               String dirPath = uuidToDir.get(slcExecution.getUuid());\r
+               return dirPath + File.separator + "SlcExecution-"\r
+                               + slcExecution.getUuid() + ".xml";\r
+       }\r
+\r
+       public void setBasePath(String basePath) {\r
+               this.basePath = basePath;\r
+       }\r
+\r
+       public void setMarshaller(Marshaller marshaller) {\r
+               this.marshaller = marshaller;\r
+       }\r
+       \r
+       \r
+}\r
index 1450cda9eb14823dfd656cf16b1e73ea1e48650c..0f2d1b66c11e65faacb6468f74112a5d27296c89 100644 (file)
@@ -4,15 +4,21 @@ import java.util.List;
 import java.util.Vector;\r
 \r
 public class SlcExecution {\r
+       public final static String STATUS_SCHEDULED = "SCHEDULED";\r
+       public final static String STATUS_RUNNING = "RUNNING";\r
+       public final static String STATUS_FINISHED = "FINISHED";\r
+       public final static String STATUS_ERROR = "ERROR";\r
+       public final static String STATUS_CLEANED = "CLEANED";\r
+\r
+       public final static String UNKOWN_HOST = "UNKOWN_HOST";\r
+\r
        private String uuid;\r
        private String host;\r
        private String path;\r
        private String type;\r
        private String status;\r
-       \r
+\r
        private List<SlcExecutionStep> steps = new Vector<SlcExecutionStep>();\r
-       \r
-       \r
 \r
        public List<SlcExecutionStep> getSteps() {\r
                return steps;\r
@@ -62,4 +68,17 @@ public class SlcExecution {
                this.status = status;\r
        }\r
 \r
+       @Override\r
+       public boolean equals(Object obj) {\r
+               if (obj instanceof SlcExecution) {\r
+                       return getUuid().equals(((SlcExecution) obj).getUuid());\r
+               }\r
+               return false;\r
+       }\r
+\r
+       @Override\r
+       public int hashCode() {\r
+               return getUuid().hashCode();\r
+       }\r
+\r
 }\r
diff --git a/org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/SlcExecutionNotifier.java b/org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/SlcExecutionNotifier.java
new file mode 100644 (file)
index 0000000..183d99a
--- /dev/null
@@ -0,0 +1,12 @@
+package org.argeo.slc.core.process;\r
+\r
+import java.util.List;\r
+\r
+public interface SlcExecutionNotifier {\r
+       public void newExecution(SlcExecution slcExecution);\r
+\r
+       public void addSteps(SlcExecution slcExecution,\r
+                       List<SlcExecutionStep> additionalSteps);\r
+\r
+       public void updateExecution(SlcExecution slcExecution);\r
+}\r
diff --git a/org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/WebServiceSlcExecutionNotifier.java b/org.argeo.slc.core/src/main/java/org/argeo/slc/core/process/WebServiceSlcExecutionNotifier.java
new file mode 100644 (file)
index 0000000..619336e
--- /dev/null
@@ -0,0 +1,38 @@
+package org.argeo.slc.core.process;\r
+\r
+import java.util.List;\r
+\r
+import org.springframework.ws.client.core.WebServiceTemplate;\r
+import org.springframework.ws.soap.client.SoapFaultClientException;\r
+\r
+import org.argeo.slc.msg.process.SlcExecutionRequest;\r
+import org.argeo.slc.msg.process.SlcExecutionStepsRequest;\r
+\r
+public class WebServiceSlcExecutionNotifier implements SlcExecutionNotifier {\r
+       private WebServiceTemplate template;\r
+\r
+       public void addSteps(SlcExecution slcExecution,\r
+                       List<SlcExecutionStep> additionalSteps) {\r
+               SlcExecutionStepsRequest req = new SlcExecutionStepsRequest();\r
+               req.setSlcExecutionUuid(slcExecution.getUuid());\r
+               req.setSteps(additionalSteps);\r
+               template.marshalSendAndReceive(req);\r
+       }\r
+\r
+       public void newExecution(SlcExecution slcExecution) {\r
+               SlcExecutionRequest req = new SlcExecutionRequest();\r
+               req.setSlcExecution(slcExecution);\r
+               template.marshalSendAndReceive(req);\r
+       }\r
+\r
+       public void updateExecution(SlcExecution slcExecution) {\r
+               SlcExecutionRequest req = new SlcExecutionRequest();\r
+               req.setSlcExecution(slcExecution);\r
+               template.marshalSendAndReceive(req);\r
+       }\r
+\r
+       public void setTemplate(WebServiceTemplate template) {\r
+               this.template = template;\r
+       }\r
+\r
+}\r
index 243e31c886b8da747029e833d47fbefa0a34dbfe..181a9febe639e6ebc2a71866615d09ed448a033a 100644 (file)
@@ -6,6 +6,7 @@ import org.argeo.slc.core.process.SlcExecution;
 \r
 public interface SlcExecutionDao {\r
        public void create(SlcExecution slcExecution);\r
+       public void update(SlcExecution slcExecution);\r
        public SlcExecution getSlcExecution(String uuid);\r
        public List<SlcExecution> listSlcExecutions();\r
 }\r
index 27656ef2a6ad41a3c6f8e69e889e2580ca781990..9518d93fc5ad804b1fc4faab27d29a14ac1bc6fa 100644 (file)
@@ -14,6 +14,10 @@ public class SlcExecutionDaoHibernate extends HibernateDaoSupport implements
                getHibernateTemplate().save(slcExecution);\r
        }\r
 \r
+       public void update(SlcExecution slcExecution) {\r
+               getHibernateTemplate().update(slcExecution);\r
+       }\r
+\r
        public SlcExecution getSlcExecution(String uuid) {\r
                return (SlcExecution) getHibernateTemplate().get(SlcExecution.class,\r
                                uuid);\r
diff --git a/org.argeo.slc.core/src/test/java/org/argeo/slc/ant/SlcAntTest.java b/org.argeo.slc.core/src/test/java/org/argeo/slc/ant/SlcAntTest.java
new file mode 100644 (file)
index 0000000..ff4eda0
--- /dev/null
@@ -0,0 +1,12 @@
+package org.argeo.slc.ant;\r
+\r
+import junit.framework.TestCase;\r
+\r
+public class SlcAntTest extends TestCase {\r
+\r
+       public void testSimpleRun() {\r
+               AntRegistryUtil.runAll(getClass().getResource(\r
+                               "/org/argeo/slc/ant/build.xml"), "test", null);\r
+       }\r
+\r
+}\r
index 3b925523b6598120390a8b09b325c49b5ba072df..8eb8b0c9c86c72ddc2f5b61d305686653e16efae 100644 (file)
@@ -7,7 +7,7 @@ log4j.logger.org.argeo.slc=DEBUG
 # Spring\r
 log4j.logger.org.springframework=INFO\r
 # Hibernate\r
-log4j.logger.org.hibernate=DEBUG\r
+log4j.logger.org.hibernate=WARN\r
 #log4j.logger.org.hibernate.SQL=TRACE\r
 #log4j.logger.org.hibernate.tool.hbm2ddl=TRACE\r
 #log4j.logger.org.hibernate.type=TRACE\r
diff --git a/org.argeo.slc.core/src/test/resources/org/argeo/slc/ant/build.xml b/org.argeo.slc.core/src/test/resources/org/argeo/slc/ant/build.xml
new file mode 100644 (file)
index 0000000..35dad68
--- /dev/null
@@ -0,0 +1,6 @@
+<project default="test">\r
+\r
+       <target name="test">\r
+               <echo message="Hello World!" />\r
+       </target>\r
+</project>
\ No newline at end of file