From a7670ff38e0eb07a827cb9508e8962a7e9d09368 Mon Sep 17 00:00:00 2001 From: Henry Lee Date: Thu, 30 Nov 2023 20:35:06 +0900 Subject: [PATCH] refactor(java): maven-war-plugin --- Java-base/maven-war-plugin/Dockerfile | 28 + Java-base/maven-war-plugin/src/Jenkinsfile | 20 + Java-base/maven-war-plugin/src/README.md | 99 ++ Java-base/maven-war-plugin/src/deploySite.sh | 23 + Java-base/maven-war-plugin/src/pom.xml | 263 ++++ .../src/src/it/MWAR-128/pom.xml | 38 + .../src/src/it/MWAR-128/prebuild.groovy | 23 + .../MWAR-128/src/main/webapp/WEB-INF/web.xml | 18 + .../src/src/it/MWAR-128/verify.groovy | 22 + .../src/src/it/MWAR-129/invoker.properties | 18 + .../src/src/it/MWAR-129/pom.xml | 121 ++ .../MWAR-129/src/main/webapp/WEB-INF/web.xml | 24 + .../src/it/MWAR-129/src/main/webapp/index.jsp | 23 + .../src/it/MWAR-129/src/main/webapp/param.jsp | 27 + .../src/src/it/MWAR-129/verify.bsh | 66 + .../src/src/it/MWAR-131/invoker.properties | 19 + .../src/src/it/MWAR-131/mwar131-test/pom.xml | 50 + .../src/main/java/com/example/App.java | 32 + .../src/test/java/com/example/AppTest.java | 62 + .../src/it/MWAR-131/mwar131-webapp/pom.xml | 59 + .../src/main/java/com/example/Util.java | 28 + .../src/main/webapp/WEB-INF/web.xml | 25 + .../mwar131-webapp/src/main/webapp/index.jsp | 23 + .../src/it/MWAR-131/mwar131-webapp2/pom.xml | 55 + .../src/main/webapp/WEB-INF/web.xml | 25 + .../mwar131-webapp2/src/main/webapp/index.jsp | 23 + .../src/src/it/MWAR-131/pom.xml | 41 + .../src/src/it/MWAR-131/verify.bsh | 77 ++ .../src/src/it/MWAR-133/invoker.properties | 18 + .../src/src/it/MWAR-133/pom.xml | 58 + .../MWAR-133/src/main/webapp/WEB-INF/web.xml | 24 + .../src/main/webresources/filtered.properties | 21 + .../src/src/it/MWAR-133/verify.bsh | 66 + .../src/src/it/MWAR-139/pom.xml | 48 + .../MWAR-139/src/main/webapp/WEB-INF/web.xml | 24 + .../src/main/webresources/filterme.xml | 23 + .../src/src/it/MWAR-139/verify.bsh | 72 ++ .../src/src/it/MWAR-143/invoker.properties | 18 + .../src/src/it/MWAR-143/pom.xml | 58 + .../src/src/it/MWAR-143/verify.bsh | 124 ++ .../src/src/it/MWAR-143/war-common/pom.xml | 52 + .../war-common/src/main/images/duke-beer.jpg | Bin 0 -> 246218 bytes .../src/main/resources/filter.properties | 21 + .../src/main/webapp/WEB-INF/web.xml | 25 + .../it/MWAR-143/war-filter-overlay/pom.xml | 65 + .../src/main/webapp/WEB-INF/web.xml | 26 + .../src/src/it/MWAR-167/invoker.properties | 18 + .../src/src/it/MWAR-167/pom.xml | 49 + .../MWAR-167/src/main/resources/MANIFEST.MF | 5 + .../src/src/it/MWAR-167/verify.bsh | 74 ++ .../src/src/it/MWAR-240/invoker.properties | 18 + .../src/src/it/MWAR-240/pom.xml | 43 + .../org/apache/maven/plugin/war/it/Dummy.java | 29 + .../src/src/it/MWAR-240/verify.bsh | 59 + .../src/src/it/MWAR-306/pom.xml | 45 + .../it/MWAR-306/src/main/webapp/index.html | 26 + .../src/src/it/MWAR-306/verify.groovy | 23 + .../src/src/it/MWAR-311/pom.xml | 64 + .../src/main/resources/app.properties | 19 + .../src/main/resources/default.properties | 18 + .../MWAR-311/src/main/webapp/WEB-INF/web.xml | 24 + .../src/src/it/MWAR-311/verify.groovy | 25 + .../src/src/it/MWAR-314/invoker.properties | 19 + .../src/src/it/MWAR-314/pom.xml | 39 + .../src/it/MWAR-314/src/main/webapp/index.jsp | 20 + .../src/src/it/MWAR-326/invoker.properties | 19 + .../src/src/it/MWAR-326/pom.xml | 46 + .../src/src/it/MWAR-326/src/main/java/A.java | 25 + .../src/src/it/MWAR-326/verify.bsh | 52 + .../src/src/it/MWAR-350/pom.xml | 69 + .../java/org/apache/maven/it0016/Person.java | 35 + .../MWAR-350/src/main/webapp/WEB-INF/web.xml | 23 + .../it/MWAR-350/src/main/webapp/index.html | 23 + .../src/src/it/MWAR-350/verify.bsh | 51 + .../src/src/it/MWAR-352_custom-webXml/pom.xml | 46 + .../src/main/webapp/WEB-INF/web.xml | 22 + .../src/main/webconfig/release/web.xml | 22 + .../it/MWAR-352_custom-webXml/verify.groovy | 23 + .../src/src/it/MWAR-371/custom/pom.xml | 65 + .../it/MWAR-371/custom/src/main/custom/a1.txt | 18 + .../it/MWAR-371/custom/src/main/custom/a2.txt | 18 + .../src/src/it/MWAR-371/generic/pom.xml | 29 + .../MWAR-371/generic/src/main/webapp/x/a1.txt | 18 + .../MWAR-371/generic/src/main/webapp/x/a2.txt | 18 + .../MWAR-371/generic/src/main/webapp/x/a3.txt | 18 + .../src/src/it/MWAR-371/pom.xml | 51 + .../src/src/it/MWAR-371/verify.groovy | 74 ++ .../MWAR-396_no-servlet30/invoker.properties | 18 + .../src/src/it/MWAR-396_no-servlet30/pom.xml | 66 + .../src/src/it/MWAR-396_servlet30/pom.xml | 66 + .../src/src/it/MWAR-396_servlet30/verify.bsh | 49 + .../invoker.properties | 20 + .../it/MWAR-427_update-without-clean/pom.xml | 79 ++ .../src/main/resources/resource.txt | 16 + .../src/main/webapp/index.html | 23 + .../verify.groovy | 26 + .../invoker.properties | 19 + .../src/it/MWAR-430_jakarta-servlet/pom.xml | 66 + .../it/MWAR-430_jakarta-servlet/verify.bsh | 49 + .../src/src/it/MWAR-62/invoker.properties | 18 + .../src/src/it/MWAR-62/pom.xml | 50 + .../it/MWAR-62/src/main/webapp/dev.properties | 18 + .../MWAR-62/src/main/webapp/test.properties | 18 + .../src/it/MWAR-62/src/main/webapp/web.xml | 24 + .../src/src/it/MWAR-62/verify.bsh | 61 + .../src/src/it/MWAR-96/pom.xml | 51 + .../src/main/filters/filter.properties | 19 + .../MWAR-96/src/main/webapp/WEB-INF/web.xml | 24 + .../src/it/MWAR-96/src/main/webapp/index.jsp | 22 + .../src/src/it/MWAR-96/verify.bsh | 74 ++ .../src/src/it/archiveClasses/pom.xml | 70 + .../java/org/apache/maven/it0016/Person.java | 35 + .../src/main/webapp/WEB-INF/web.xml | 23 + .../archiveClasses/src/main/webapp/index.html | 23 + .../src/src/it/archiveClasses/verify.bsh | 99 ++ .../src/src/it/default/pom.xml | 66 + .../java/org/apache/maven/it0016/Person.java | 35 + .../default/src/main/webapp/WEB-INF/web.xml | 23 + .../src/it/default/src/main/webapp/index.html | 23 + .../src/src/it/default/verify.bsh | 97 ++ .../src/src/it/manifest-content/pom.xml | 50 + .../src/main/webapp/index.jsp | 20 + .../src/src/it/manifest-content/verify.bsh | 96 ++ .../it/overlay-excludes/invoker.properties | 18 + .../src/src/it/overlay-excludes/pom.xml | 44 + .../src/src/it/overlay-excludes/verify.bsh | 31 + .../war-exclude-overlay/pom.xml | 61 + .../src/main/webapp/WEB-INF/web.xml | 26 + .../it/overlay-excludes/war-overlay/pom.xml | 30 + .../src/main/webapp/WEB-INF/web.xml | 25 + .../src/main/webapp/lib/js/something/else.js | 20 + .../invoker.properties | 18 + .../src/it/overlay-keeps-contextxml/pom.xml | 44 + .../it/overlay-keeps-contextxml/verify.bsh | 31 + .../war1-with-contextxml/pom.xml | 31 + .../src/main/webapp/META-INF/context.xml | 19 + .../src/main/webapp/WEB-INF/web.xml | 25 + .../war2-result/pom.xml | 40 + .../src/main/webapp/WEB-INF/web.xml | 25 + .../invoker.properties | 18 + .../scoped-dependency-same-artifact/pom.xml | 53 + .../src/main/webapp/WEB-INF/web.xml | 29 + .../src/main/webapp/index.jsp | 20 + .../verify.bsh | 88 ++ .../maven-war-plugin/src/src/it/settings.xml | 55 + .../simple-war-no-webxml/invoker.properties | 19 + .../src/src/it/simple-war-no-webxml/pom.xml | 47 + .../src/main/webapp/index.jsp | 20 + .../src/it/simple-war-no-webxml/verify.bsh | 82 ++ .../it/simple-war-project/invoker.properties | 18 + .../src/src/it/simple-war-project/pom.xml | 54 + .../src/main/webapp/WEB-INF/web.xml | 29 + .../src/main/webapp/index.jsp | 20 + .../src/src/it/simple-war-project/verify.bsh | 96 ++ .../parent/pom.xml | 62 + .../web-resources-filtering-delimiter/pom.xml | 44 + .../verify.bsh | 108 ++ .../web/pom.xml | 72 ++ .../web/src/main/webapp/WEB-INF/web.xml | 26 + .../web/src/main/webapp/index.jsp | 23 + .../WEB-INF/classes/my.properties | 18 + .../main/webresources/WEB-INF/jetty-env.xml | 40 + .../it/web-resources-filtering/parent/pom.xml | 62 + .../src/it/web-resources-filtering/pom.xml | 44 + .../src/it/web-resources-filtering/verify.bsh | 115 ++ .../it/web-resources-filtering/web/pom.xml | 68 + .../web/src/main/webapp/WEB-INF/web.xml | 26 + .../web/src/main/webapp/index.jsp | 23 + .../WEB-INF/classes/my.properties | 18 + .../main/webresources/WEB-INF/jetty-env.xml | 44 + .../maven/plugins/war/AbstractWarMojo.java | 1152 +++++++++++++++++ .../org/apache/maven/plugins/war/Overlay.java | 384 ++++++ .../maven/plugins/war/WarExplodedMojo.java | 46 + .../maven/plugins/war/WarInPlaceMojo.java | 43 + .../org/apache/maven/plugins/war/WarMojo.java | 574 ++++++++ .../plugins/war/overlay/DefaultOverlay.java | 62 + .../InvalidOverlayConfigurationException.java | 54 + .../plugins/war/overlay/OverlayManager.java | 253 ++++ .../packaging/AbstractWarPackagingTask.java | 492 +++++++ .../war/packaging/ArtifactsPackagingTask.java | 187 +++ .../war/packaging/ClassesPackagingTask.java | 128 ++ .../war/packaging/CopyUserManifestTask.java | 77 ++ .../war/packaging/OverlayPackagingTask.java | 157 +++ .../war/packaging/WarPackagingContext.java | 253 ++++ .../war/packaging/WarPackagingTask.java | 45 + .../packaging/WarProjectPackagingTask.java | 381 ++++++ .../plugins/war/util/ClassesPackager.java | 88 ++ .../plugins/war/util/DependencyInfo.java | 103 ++ .../maven/plugins/war/util/PathSet.java | 264 ++++ .../maven/plugins/war/util/WarUtils.java | 105 ++ .../plugins/war/util/WebappStructure.java | 391 ++++++ .../META-INF/plexus/components.xml | 89 ++ .../adding-filtering-webresources.apt.vm | 407 ++++++ .../site/apt/examples/file-name-mapping.apt | 60 + .../including-excluding-files-from-war.apt.vm | 94 ++ .../examples/rapid-testing-jetty6-plugin.apt | 75 ++ .../src/site/apt/examples/skinny-wars.apt.vm | 112 ++ .../apt/examples/war-manifest-guide.apt.vm | 102 ++ .../src/src/site/apt/index.apt.vm | 98 ++ .../src/src/site/apt/overlays.apt.vm | 393 ++++++ .../src/src/site/apt/usage.apt.vm | 229 ++++ .../src/src/site/fml/faq.fml.vm | 141 ++ .../src/src/site/resources/download.cgi | 22 + .../maven-war-plugin/src/src/site/site.xml | 48 + .../src/src/site/xdoc/download.xml.vm | 126 ++ .../war/AbstractWarExplodedMojoTest.java | 303 +++++ .../plugins/war/AbstractWarMojoTest.java | 332 +++++ .../war/WarExplodedMojoFilteringTest.java | 209 +++ .../plugins/war/WarExplodedMojoTest.java | 1055 +++++++++++++++ .../maven/plugins/war/WarInPlaceMojoTest.java | 90 ++ .../apache/maven/plugins/war/WarMojoTest.java | 535 ++++++++ .../maven/plugins/war/WarOverlaysTest.java | 494 +++++++ .../apache/maven/plugins/war/WarZipTest.java | 182 +++ .../war/overlay/OverlayManagerTest.java | 226 ++++ .../plugins/war/stub/AarArtifactStub.java | 79 ++ .../war/stub/AbstractArtifactStub.java | 166 +++ .../plugins/war/stub/EJBArtifactStub.java | 79 ++ .../stub/EJBArtifactStubWithClassifier.java | 90 ++ .../war/stub/EJBClientArtifactStub.java | 84 ++ .../stub/IncludeExcludeWarArtifactStub.java | 42 + .../plugins/war/stub/JarArtifactStub.java | 151 +++ .../plugins/war/stub/MarArtifactStub.java | 78 ++ .../stub/MavenProject4CopyConstructor.java | 64 + .../war/stub/MavenProjectArtifactsStub.java | 85 ++ .../war/stub/MavenProjectBasicStub.java | 130 ++ .../plugins/war/stub/MavenZipProject.java | 58 + .../maven/plugins/war/stub/ModelStub.java | 100 ++ .../plugins/war/stub/PARArtifactStub.java | 46 + .../plugins/war/stub/ProjectHelperStub.java | 81 ++ .../maven/plugins/war/stub/ResourceStub.java | 57 + .../plugins/war/stub/TLDArtifactStub.java | 46 + .../plugins/war/stub/WarArtifact4CCStub.java | 57 + .../plugins/war/stub/WarArtifactStub.java | 115 ++ .../plugins/war/stub/WarOverlayStub.java | 76 ++ .../plugins/war/stub/XarArtifactStub.java | 79 ++ .../plugins/war/stub/ZipArtifactStub.java | 89 ++ .../maven/plugins/war/util/PathSetTest.java | 257 ++++ .../plugins/war/util/WebappStructureTest.java | 114 ++ .../overlays/overlay-one/WEB-INF/web.xml | 21 + .../resources/overlays/overlay-one/index.jsp | 25 + .../resources/overlays/overlay-one/login.jsp | 21 + .../overlays/overlay-two/WEB-INF/web.xml | 21 + .../resources/overlays/overlay-two/admin.jsp | 21 + .../resources/overlays/overlay-two/index.jsp | 25 + .../test/resources/unit/sample_wars/ejb.jar | Bin 0 -> 753 bytes .../resources/unit/sample_wars/ejbclient.jar | Bin 0 -> 753 bytes .../unit/sample_wars/include-exclude.war | Bin 0 -> 1255 bytes .../sample_wars/javax.servlet-api-3.0.1.jar | Bin 0 -> 85353 bytes .../resources/unit/sample_wars/sample.par | 0 .../unit/sample_wars/simple-updated.war | Bin 0 -> 995 bytes .../resources/unit/sample_wars/simple.aar | Bin 0 -> 969 bytes .../resources/unit/sample_wars/simple.jar | Bin 0 -> 969 bytes .../resources/unit/sample_wars/simple.mar | 0 .../resources/unit/sample_wars/simple.war | Bin 0 -> 969 bytes .../resources/unit/sample_wars/simple.xar | 0 .../test/resources/unit/sample_wars/tld.jar | 0 .../warexplodedinplacemojo/plugin-config.xml | 31 + .../unit/warexplodedmojo/plugin-config.xml | 31 + .../unit/warmojotest/not-primary-artifact.xml | 32 + .../plugin-config-primary-artifact.xml | 32 + .../resources/unit/waroverlays/default.xml | 32 + .../test/resources/unit/warziptest/foobar.zip | Bin 0 -> 453 bytes .../unit/warziptest/war-with-zip.xml | 31 + .../Dockerfile | 18 + .../buggy.java | 147 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 148 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 304 +++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 302 +++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 506 ++++++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 69 + .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 149 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 146 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 146 +++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 256 ++++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 139 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 110 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 110 ++ .../metadata.json | 21 + .../npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 110 ++ .../metadata.json | 21 + .../npe.json | 7 + Java/maven-war-plugin-WarMojo_349/Dockerfile | 18 + Java/maven-war-plugin-WarMojo_349/buggy.java | 583 +++++++++ .../metadata.json | 21 + Java/maven-war-plugin-WarMojo_349/npe.json | 7 + .../Dockerfile | 18 + .../buggy.java | 380 ++++++ .../metadata.json | 21 + .../npe.json | 7 + 327 files changed, 24470 insertions(+) create mode 100644 Java-base/maven-war-plugin/Dockerfile create mode 100644 Java-base/maven-war-plugin/src/Jenkinsfile create mode 100644 Java-base/maven-war-plugin/src/README.md create mode 100755 Java-base/maven-war-plugin/src/deploySite.sh create mode 100644 Java-base/maven-war-plugin/src/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-128/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-128/prebuild.groovy create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-128/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-128/verify.groovy create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-129/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-129/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/WEB-INF/web.xml create mode 100755 Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/index.jsp create mode 100755 Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/param.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-129/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/main/java/com/example/App.java create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/test/java/com/example/AppTest.java create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/java/com/example/Util.java create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-131/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-133/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-133/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webresources/filtered.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-133/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-139/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webresources/filterme.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-139/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/pom.xml create mode 100755 Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/images/duke-beer.jpg create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/resources/filter.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-167/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-167/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-167/src/main/resources/MANIFEST.MF create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-167/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-240/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-240/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-240/src/main/java/org/apache/maven/plugin/war/it/Dummy.java create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-240/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-306/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-306/src/main/webapp/index.html create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-306/verify.groovy create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-311/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/app.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/default.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-311/verify.groovy create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-314/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-314/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-314/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-326/invoker.properties create mode 100755 Java-base/maven-war-plugin/src/src/it/MWAR-326/pom.xml create mode 100755 Java-base/maven-war-plugin/src/src/it/MWAR-326/src/main/java/A.java create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-326/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-350/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/java/org/apache/maven/it0016/Person.java create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/index.html create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-350/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webconfig/release/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/verify.groovy create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a1.txt create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a2.txt create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a1.txt create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a2.txt create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a3.txt create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-371/verify.groovy create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/resources/resource.txt create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/webapp/index.html create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/verify.groovy create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-62/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-62/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/dev.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/test.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-62/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-96/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/filters/filter.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/WEB-INF/web.xml create mode 100755 Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/MWAR-96/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/archiveClasses/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/java/org/apache/maven/it0016/Person.java create mode 100644 Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/index.html create mode 100644 Java-base/maven-war-plugin/src/src/it/archiveClasses/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/default/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/default/src/main/java/org/apache/maven/it0016/Person.java create mode 100644 Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/index.html create mode 100644 Java-base/maven-war-plugin/src/src/it/default/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/manifest-content/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/manifest-content/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/manifest-content/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/lib/js/something/else.js create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/META-INF/context.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/settings.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-project/invoker.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-project/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/WEB-INF/web.xml create mode 100755 Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/simple-war-project/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/parent/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/WEB-INF/web.xml create mode 100755 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/classes/my.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/jetty-env.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/parent/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/verify.bsh create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/pom.xml create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/WEB-INF/web.xml create mode 100755 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/classes/my.properties create mode 100644 Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/jetty-env.xml create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/Overlay.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarExplodedMojo.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarInPlaceMojo.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarMojo.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/DefaultOverlay.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/InvalidOverlayConfigurationException.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/OverlayManager.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/AbstractWarPackagingTask.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ArtifactsPackagingTask.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ClassesPackagingTask.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/CopyUserManifestTask.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/OverlayPackagingTask.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingContext.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingTask.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarProjectPackagingTask.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/ClassesPackager.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/DependencyInfo.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/PathSet.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WarUtils.java create mode 100644 Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WebappStructure.java create mode 100644 Java-base/maven-war-plugin/src/src/main/resources-filtered/META-INF/plexus/components.xml create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/examples/adding-filtering-webresources.apt.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/examples/file-name-mapping.apt create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/examples/including-excluding-files-from-war.apt.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/examples/rapid-testing-jetty6-plugin.apt create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/examples/skinny-wars.apt.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/examples/war-manifest-guide.apt.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/index.apt.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/overlays.apt.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/apt/usage.apt.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/fml/faq.fml.vm create mode 100644 Java-base/maven-war-plugin/src/src/site/resources/download.cgi create mode 100644 Java-base/maven-war-plugin/src/src/site/site.xml create mode 100644 Java-base/maven-war-plugin/src/src/site/xdoc/download.xml.vm create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarMojoTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoFilteringTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarInPlaceMojoTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarMojoTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarOverlaysTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarZipTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/overlay/OverlayManagerTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AarArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStubWithClassifier.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBClientArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/IncludeExcludeWarArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MarArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProject4CopyConstructor.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectArtifactsStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectBasicStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenZipProject.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ModelStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/PARArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ProjectHelperStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ResourceStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/TLDArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifact4CCStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarOverlayStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/XarArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ZipArtifactStub.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/PathSetTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/WebappStructureTest.java create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/login.jsp create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/WEB-INF/web.xml create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/admin.jsp create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/index.jsp create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/ejb.jar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/ejbclient.jar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/include-exclude.war create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/javax.servlet-api-3.0.1.jar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/sample.par create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple-updated.war create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.aar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.jar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.mar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.war create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.xar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/tld.jar create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedinplacemojo/plugin-config.xml create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedmojo/plugin-config.xml create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/not-primary-artifact.xml create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/plugin-config-primary-artifact.xml create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/waroverlays/default.xml create mode 100755 Java-base/maven-war-plugin/src/src/test/resources/unit/warziptest/foobar.zip create mode 100644 Java-base/maven-war-plugin/src/src/test/resources/unit/warziptest/war-with-zip.xml create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_103/Dockerfile create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_103/buggy.java create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_103/metadata.json create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_103/npe.json create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_76/Dockerfile create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_76/buggy.java create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_76/metadata.json create mode 100644 Java/maven-war-plugin-AbstractArtifactStub_76/npe.json create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/Dockerfile create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/buggy.java create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/metadata.json create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/npe.json create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/Dockerfile create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/buggy.java create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/metadata.json create mode 100644 Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/npe.json create mode 100644 Java/maven-war-plugin-AbstractWarPackagingTask_99/Dockerfile create mode 100644 Java/maven-war-plugin-AbstractWarPackagingTask_99/buggy.java create mode 100644 Java/maven-war-plugin-AbstractWarPackagingTask_99/metadata.json create mode 100644 Java/maven-war-plugin-AbstractWarPackagingTask_99/npe.json create mode 100644 Java/maven-war-plugin-CopyUserManifestTask_59/Dockerfile create mode 100644 Java/maven-war-plugin-CopyUserManifestTask_59/buggy.java create mode 100644 Java/maven-war-plugin-CopyUserManifestTask_59/metadata.json create mode 100644 Java/maven-war-plugin-CopyUserManifestTask_59/npe.json create mode 100644 Java/maven-war-plugin-JarArtifactStub_135/Dockerfile create mode 100644 Java/maven-war-plugin-JarArtifactStub_135/buggy.java create mode 100644 Java/maven-war-plugin-JarArtifactStub_135/metadata.json create mode 100644 Java/maven-war-plugin-JarArtifactStub_135/npe.json create mode 100644 Java/maven-war-plugin-JarArtifactStub_79/Dockerfile create mode 100644 Java/maven-war-plugin-JarArtifactStub_79/buggy.java create mode 100644 Java/maven-war-plugin-JarArtifactStub_79/metadata.json create mode 100644 Java/maven-war-plugin-JarArtifactStub_79/npe.json create mode 100644 Java/maven-war-plugin-JarArtifactStub_91/Dockerfile create mode 100644 Java/maven-war-plugin-JarArtifactStub_91/buggy.java create mode 100644 Java/maven-war-plugin-JarArtifactStub_91/metadata.json create mode 100644 Java/maven-war-plugin-JarArtifactStub_91/npe.json create mode 100644 Java/maven-war-plugin-OverlayManager_200/Dockerfile create mode 100644 Java/maven-war-plugin-OverlayManager_200/buggy.java create mode 100644 Java/maven-war-plugin-OverlayManager_200/metadata.json create mode 100644 Java/maven-war-plugin-OverlayManager_200/npe.json create mode 100644 Java/maven-war-plugin-OverlayPackagingTask_80/Dockerfile create mode 100644 Java/maven-war-plugin-OverlayPackagingTask_80/buggy.java create mode 100644 Java/maven-war-plugin-OverlayPackagingTask_80/metadata.json create mode 100644 Java/maven-war-plugin-OverlayPackagingTask_80/npe.json create mode 100644 Java/maven-war-plugin-WarArtifactStub_51/Dockerfile create mode 100644 Java/maven-war-plugin-WarArtifactStub_51/buggy.java create mode 100644 Java/maven-war-plugin-WarArtifactStub_51/metadata.json create mode 100644 Java/maven-war-plugin-WarArtifactStub_51/npe.json create mode 100644 Java/maven-war-plugin-WarArtifactStub_69/Dockerfile create mode 100644 Java/maven-war-plugin-WarArtifactStub_69/buggy.java create mode 100644 Java/maven-war-plugin-WarArtifactStub_69/metadata.json create mode 100644 Java/maven-war-plugin-WarArtifactStub_69/npe.json create mode 100644 Java/maven-war-plugin-WarArtifactStub_86/Dockerfile create mode 100644 Java/maven-war-plugin-WarArtifactStub_86/buggy.java create mode 100644 Java/maven-war-plugin-WarArtifactStub_86/metadata.json create mode 100644 Java/maven-war-plugin-WarArtifactStub_86/npe.json create mode 100644 Java/maven-war-plugin-WarMojo_349/Dockerfile create mode 100644 Java/maven-war-plugin-WarMojo_349/buggy.java create mode 100644 Java/maven-war-plugin-WarMojo_349/metadata.json create mode 100644 Java/maven-war-plugin-WarMojo_349/npe.json create mode 100644 Java/maven-war-plugin-WarProjectPackagingTask_327/Dockerfile create mode 100644 Java/maven-war-plugin-WarProjectPackagingTask_327/buggy.java create mode 100644 Java/maven-war-plugin-WarProjectPackagingTask_327/metadata.json create mode 100644 Java/maven-war-plugin-WarProjectPackagingTask_327/npe.json diff --git a/Java-base/maven-war-plugin/Dockerfile b/Java-base/maven-war-plugin/Dockerfile new file mode 100644 index 000000000..e208c4890 --- /dev/null +++ b/Java-base/maven-war-plugin/Dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:22.04 + +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update \ + && apt-get install -y software-properties-common \ + && add-apt-repository ppa:deadsnakes/ppa \ + && apt-get update \ + && apt-get install -y \ + build-essential \ + git \ + vim \ + jq \ + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/list/* + +RUN apt-get -y install sudo \ + openjdk-8-jdk \ + maven + +RUN bash -c "echo 2 | update-alternatives --config java" + +COPY src /workspace +WORKDIR /workspace + +RUN mvn install -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false + +RUN mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 + +ENV TZ=Asia/Seoul diff --git a/Java-base/maven-war-plugin/src/Jenkinsfile b/Java-base/maven-war-plugin/src/Jenkinsfile new file mode 100644 index 000000000..e9f05f7d9 --- /dev/null +++ b/Java-base/maven-war-plugin/src/Jenkinsfile @@ -0,0 +1,20 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +asfMavenTlpPlgnBuild() diff --git a/Java-base/maven-war-plugin/src/README.md b/Java-base/maven-war-plugin/src/README.md new file mode 100644 index 000000000..19bf10233 --- /dev/null +++ b/Java-base/maven-war-plugin/src/README.md @@ -0,0 +1,99 @@ + +Contributing to [Apache Maven WAR Plugin](https://maven.apache.org/plugins/maven-war-plugin/) +====================== + +[![ASF Jira](https://img.shields.io/endpoint?url=https%3A%2F%2Fmaven.apache.org%2Fbadges%2Fasf_jira-MWAR.json)][jira] +[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)][license] +[![Maven Central](https://img.shields.io/maven-central/v/org.apache.maven.plugins/maven-war-plugin.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.apache.maven.plugins/maven-war-plugin) +[![Jenkins Status](https://img.shields.io/jenkins/s/https/builds.apache.org/job/maven-box/job/maven-war-plugin/job/master.svg?)][build] +[![Jenkins tests](https://img.shields.io/jenkins/t/https/builds.apache.org/job/maven-box/job/maven-war-plugin/job/master.svg?)][test-results] + + +You have found a bug or you have an idea for a cool new feature? Contributing +code is a great way to give something back to the open source community. Before +you dig right into the code, there are a few guidelines that we need +contributors to follow so that we can have a chance of keeping on top of +things. + +Getting Started +--------------- + ++ Make sure you have a [JIRA account](https://issues.apache.org/jira/). ++ Make sure you have a [GitHub account](https://github.com/signup/free). ++ If you're planning to implement a new feature, it makes sense to discuss your changes + on the [dev list][ml-list] first. + This way you can make sure you're not wasting your time on something that isn't + considered to be in Apache Maven's scope. ++ Submit a ticket for your issue, assuming one does not already exist. + + Clearly describe the issue, including steps to reproduce when it is a bug. + + Make sure you fill in the earliest version that you know has the issue. ++ Fork the repository on GitHub. + +Making and Submitting Changes +-------------- + +We accept Pull Requests via GitHub. The [developer mailing list][ml-list] is the +main channel of communication for contributors. +There are some guidelines which will make applying PRs easier for us: ++ Create a topic branch from where you want to base your work (this is usually the master branch). + Push your changes to a topic branch in your fork of the repository. ++ Make commits of logical units. ++ Respect the original code style: by using the same [codestyle][code-style], + patches should only highlight the actual difference, not being disturbed by any formatting issues: + + Only use spaces for indentation. + + Create minimal diffs - disable on save actions like reformat source code or organize imports. + If you feel the source code should be reformatted, create a separate PR for this change. + + Check for unnecessary whitespace with `git diff --check` before committing. ++ Make sure your commit messages are in the proper format. Your commit message should contain the key of the JIRA issue. +``` +[MWAR-XXX] - Subject of the JIRA Ticket + Optional supplemental description. +``` ++ Make sure you have added the necessary tests (JUnit/IT) for your changes. ++ Run all the tests with `mvn -Prun-its verify` to assure nothing else was accidentally broken. ++ Submit a pull request to the repository in the Apache organization. ++ Update your JIRA ticket and include a link to the pull request in the ticket. + +If you plan to contribute on a regular basis, please consider filing a [contributor license agreement][cla]. + +Making Trivial Changes +---------------------- + +For changes of a trivial nature to comments and documentation, it is not always +necessary to create a new ticket in JIRA. In this case, it is appropriate to +start the first line of a commit with '(doc)' instead of a ticket number. + +Additional Resources +-------------------- + ++ [Contributing patches](https://maven.apache.org/guides/development/guide-maven-development.html#Creating_and_submitting_a_patch) ++ [Apache Maven WAR JIRA project page][jira] ++ [Contributor License Agreement][cla] ++ [General GitHub documentation](https://help.github.com/) ++ [GitHub pull request documentation](https://help.github.com/send-pull-requests/) ++ [Apache Maven Twitter Account](https://twitter.com/ASFMavenProject) ++ #Maven IRC channel on freenode.org + +[jira]: https://issues.apache.org/jira/projects/MWAR/ +[license]: https://www.apache.org/licenses/LICENSE-2.0 +[ml-list]: https://maven.apache.org/mailing-lists.html +[code-style]: https://maven.apache.org/developers/conventions/code.html +[cla]: https://www.apache.org/licenses/#clas +[maven-wiki]: https://cwiki.apache.org/confluence/display/MAVEN/Index +[test-results]: https://builds.apache.org/job/maven-box/job/maven-war-plugin/job/master/lastCompletedBuild/testReport/ +[build]: https://builds.apache.org/job/maven-box/job/maven-war-plugin/job/master/ diff --git a/Java-base/maven-war-plugin/src/deploySite.sh b/Java-base/maven-war-plugin/src/deploySite.sh new file mode 100755 index 000000000..f6c265d75 --- /dev/null +++ b/Java-base/maven-war-plugin/src/deploySite.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +mvn -Preporting site site:stage $@ +mvn scm-publish:publish-scm $@ diff --git a/Java-base/maven-war-plugin/src/pom.xml b/Java-base/maven-war-plugin/src/pom.xml new file mode 100644 index 000000000..7256fb4ac --- /dev/null +++ b/Java-base/maven-war-plugin/src/pom.xml @@ -0,0 +1,263 @@ + + + + + + 4.0.0 + + + maven-plugins + org.apache.maven.plugins + 34 + + + + maven-war-plugin + 3.3.1-SNAPSHOT + maven-plugin + + Apache Maven WAR Plugin + Builds a Web Application Archive (WAR) file from the project output and its dependencies. + + + ${mavenVersion} + + + + scm:git:https://gitbox.apache.org/repos/asf/maven-war-plugin.git + scm:git:https://gitbox.apache.org/repos/asf/maven-war-plugin.git + https://github.com/apache/maven-war-plugin/tree/${project.scm.tag} + HEAD + + + JIRA + https://issues.apache.org/jira/browse/MWAR + + + Jenkins + https://builds.apache.org/job/maven-box/job/maven-war-plugin/ + + + + apache.website + scm:svn:https://svn.apache.org/repos/asf/maven/website/components/${maven.site.path} + + + + + 3.5.0 + 3.1.1 + 3.0 + 7 + 2020-06-06T06:50:15Z + + + + + Auke Schrijnen + + + Ludwig Magnusson + + + Hayarobi Park + + + Enrico Olivelli + + + + + + org.apache.maven + maven-plugin-api + ${mavenVersion} + + + org.apache.maven + maven-core + ${mavenVersion} + + + org.apache.maven + maven-archiver + ${mavenArchiverVersion} + + + org.apache.maven.plugin-tools + maven-plugin-annotations + provided + + + commons-io + commons-io + 2.6 + + + org.codehaus.plexus + plexus-archiver + 4.2.2 + + + org.codehaus.plexus + plexus-interpolation + 1.26 + + + + org.codehaus.plexus + plexus-utils + 3.3.0 + + + + org.apache.maven.shared + maven-filtering + ${mavenFilteringVersion} + + + + org.apache.maven.shared + maven-mapping + 3.0.0 + + + + org.apache.maven + maven-compat + ${mavenVersion} + test + + + junit + junit + 4.13 + test + + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 2.1 + test + + + + + + + src/main/resources-filtered + true + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-bytecode-version + + enforce + + + + + ${maven.compiler.target} + + + true + + + + + + org.apache.rat + apache-rat-plugin + + + + src/it/MWAR-167/src/main/resources/MANIFEST.MF + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${project.build.directory} + ${project.build.outputDirectory} + + + + + + + + + run-its + + + + org.apache.maven.plugins + maven-invoker-plugin + + + clean + package + + src/it + verify + prebuild + ${project.build.directory}/local-repo + src/it/settings.xml + ${project.build.directory}/it + + + + install + pre-integration-tests + + install + + + + javax.servlet:servlet-api:2.4:jar + javax.servlet:javax.servlet-api:3.0.1:jar + org.apache.struts:struts-core:1.3.9:jar + org.codehaus.plexus:plexus-utils:1.4.7:jar:sources + + + + + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-128/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-128/pom.xml new file mode 100644 index 000000000..cb2bfc482 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-128/pom.xml @@ -0,0 +1,38 @@ + + + + 4.0.0 + testwar + MWAR-128 + war + 1.0-SNAPSHOT + + + + + maven-war-plugin + @project.version@ + + true + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-128/prebuild.groovy b/Java-base/maven-war-plugin/src/src/it/MWAR-128/prebuild.groovy new file mode 100644 index 000000000..f0f6e037d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-128/prebuild.groovy @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +def dir = new File( basedir, "src/main/webapp/WEB-INF/logs" ) +dir.mkdirs() +return true; + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-128/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-128/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..468ed7856 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-128/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,18 @@ + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-128/verify.groovy b/Java-base/maven-war-plugin/src/src/it/MWAR-128/verify.groovy new file mode 100644 index 000000000..9a8843c2f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-128/verify.groovy @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +def warFile = new java.util.jar.JarFile( new File( basedir, "target/MWAR-128-1.0-SNAPSHOT.war" ), false ); +assert warFile.getEntry( 'WEB-INF/logs' ) != null + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-129/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-129/invoker.properties new file mode 100644 index 000000000..7e6fbfd9f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-129/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean war:exploded diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-129/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-129/pom.xml new file mode 100644 index 000000000..279a8489d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-129/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + testwar + MWAR-129 + war + 1.0-SNAPSHOT + MWAR-129 Maven Webapp + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + + package + + + true + src/main/resources + + + src/main/java + + **/*.xml + + + + + + src/test/resources + + + src/main/webapp + + **/*.xml + + + + src/test/webapp + + **/*.xml + + + + ${project.artifactId} + + + maven-war-plugin + + + @pom.version@ + + src/main/webapp + + + true + src/main/webapp + . + + param.jsp + + + + + + + + + + profile1 + + + profile1 + + + + profile1 + + + profile1.css + + + + profile2 + + true + + profile2 + + + + profile2 + + + profile2.css + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..6a8fa709e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/index.jsp new file mode 100755 index 000000000..9c9f33459 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ + + + +

Hello World!

+ + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/param.jsp b/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/param.jsp new file mode 100755 index 000000000..6be3a1be0 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-129/src/main/webapp/param.jsp @@ -0,0 +1,27 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-129/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-129/verify.bsh new file mode 100644 index 000000000..7b85cd782 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-129/verify.bsh @@ -0,0 +1,66 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + + File webappDirectory = new File( target, "profile2" ); + if ( !webappDirectory.exists() || !webappDirectory.isDirectory() ) + { + System.err.println( "webappDirectory is missing or not a directory." ); + return false; + } + + File param = new File( webappDirectory, "param.jsp" ); + if ( !param.exists() || param.isDirectory() ) + { + System.err.println( "param.jsp file is missing or a directory." ); + return false; + } + System.out.println( " before reading param " ); + String paramContent = FileUtils.fileRead( param ); + + + int indexOf = paramContent.indexOf( "" ); + if ( indexOf < 0 ) + { + System.err.println( "param.jsp not contains " ); + return false; + } + +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-131/invoker.properties new file mode 100644 index 000000000..f93d074f8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/invoker.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean install +invoker.maven.version=2.0.9+ \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/pom.xml new file mode 100644 index 000000000..b1b1d0559 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/pom.xml @@ -0,0 +1,50 @@ + + + + + mwar131 + com.example + 1.0-SNAPSHOT + + 4.0.0 + mwar131-test + Maven Quick Start Archetype + + + Example project that expects the attached jar from mwar131-webapp to be on the compile and test classpath. + + + 1.0-SNAPSHOT + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + com.example + mwar131-webapp + 1.0-SNAPSHOT + classes + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/main/java/com/example/App.java b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/main/java/com/example/App.java new file mode 100644 index 000000000..d5290dfd6 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/main/java/com/example/App.java @@ -0,0 +1,32 @@ +package com.example; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World is " + Util.isPresent() ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/test/java/com/example/AppTest.java b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/test/java/com/example/AppTest.java new file mode 100644 index 000000000..0c3fbb1cd --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-test/src/test/java/com/example/AppTest.java @@ -0,0 +1,62 @@ +package com.example; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } + + public void testUtil() + { + assertTrue( Util.isPresent() ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/pom.xml new file mode 100644 index 000000000..ee5ac62f5 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + + com.example + mwar131 + 1.0-SNAPSHOT + + mwar131-webapp + war + MWAR-131 Webapp + + Web application with classes that get installed as an + attached artifact with a classifier (mwar131-webapp-1.0-SNAPSHOT-classes.jar) + + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + org.apache.struts + struts-core + 1.3.9 + + + + mwar131-webapp + + + maven-war-plugin + + true + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/java/com/example/Util.java b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/java/com/example/Util.java new file mode 100644 index 000000000..b97118786 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/java/com/example/Util.java @@ -0,0 +1,28 @@ +package com.example; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public class Util +{ + public static boolean isPresent() + { + return true; + } +} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..ef9d13e76 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/index.jsp new file mode 100644 index 000000000..9c9f33459 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ + + + +

Hello World!

+ + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/pom.xml new file mode 100644 index 000000000..0125a0bd2 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/pom.xml @@ -0,0 +1,55 @@ + + + + + mwar131 + com.example + 1.0-SNAPSHOT + + 4.0.0 + mwar131-webapp2 + war + Maven Webapp Archetype + + + Webapp that declares the attached jar from mwar131-webapp as a dependency, which should show up in WEB-INF/lib + along with its transitive dependencies. + + + 1.0-SNAPSHOT + http://maven.apache.org + + ${project.artifactId} + + + + junit + junit + 3.8.1 + test + + + com.example + mwar131-webapp + 1.0-SNAPSHOT + classes + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..ef9d13e76 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/index.jsp new file mode 100644 index 000000000..9c9f33459 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/mwar131-webapp2/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ + + + +

Hello World!

+ + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-131/pom.xml new file mode 100644 index 000000000..cc2bbca4a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/pom.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + com.example + mwar131 + pom + 1.0-SNAPSHOT + MWAR-131 Integration Test + http://maven.apache.org + + + + maven-war-plugin + @pom.version@ + + + + + mwar131-webapp + mwar131-test + mwar131-webapp2 + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-131/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-131/verify.bsh new file mode 100644 index 000000000..ca9fe0a5d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-131/verify.bsh @@ -0,0 +1,77 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + + // Make sure the -classes jar from the first webapp is installed into the local repo + File localRepoClassesJar = new File( basedir, + "../../../target/local-repo/com/example/mwar131-webapp/1.0-SNAPSHOT/mwar131-webapp-1.0-SNAPSHOT-classes.jar"); + + if ( !localRepoClassesJar.exists() || localRepoClassesJar.isDirectory() ) + { + System.err.println( "The -classes jar file is missing or is a directory." ); + return false; + } + + // Make sure the -classes jar is included in WEB-INF/lib of the second webapp + File classesJar = new File( basedir, + "mwar131-webapp2/target/mwar131-webapp2/WEB-INF/lib/mwar131-webapp-1.0-SNAPSHOT-classes.jar"); + + if ( !classesJar.exists() || classesJar.isDirectory() ) + { + System.err.println( "The -classes jar file is missing or is a directory." ); + return false; + } + + // Make sure dependencies of the -classes jar are included in WEB-INF/lib of the second webapp + File strutsJar = new File( basedir, + "mwar131-webapp2/target/mwar131-webapp2/WEB-INF/lib/struts-core-1.3.9.jar"); + + if ( !strutsJar.exists() || strutsJar.isDirectory() ) + { + System.err.println( "The Struts 1.3.9 jar file is missing or is a directory." ); + return false; + } + + // Make sure transitive dependencies of the -classes jar are included in WEB-INF/lib of the second webapp + File digesterJar = new File( basedir, + "mwar131-webapp2/target/mwar131-webapp2/WEB-INF/lib/commons-digester-1.8.jar"); + + if ( !digesterJar.exists() || digesterJar.isDirectory() ) + { + System.err.println( "The Commons Digester 1.8 jar file is missing or is a directory." ); + return false; + } + +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-133/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-133/invoker.properties new file mode 100644 index 000000000..7e6fbfd9f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-133/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean war:exploded diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-133/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-133/pom.xml new file mode 100644 index 000000000..99248bc3b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-133/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + testwar + MWAR-133 + war + 1.0-SNAPSHOT + MWAR-133 Maven Webapp + MWAR-133 it + + + + + src/main/resources + true + + + + + maven-war-plugin + @pom.version@ + + + + src/main/webresources + true + + + + + + + + + org.codehaus.plexus + plexus-utils + 1.4.6 + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..6a8fa709e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webresources/filtered.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webresources/filtered.properties new file mode 100644 index 000000000..d60a845f6 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-133/src/main/webresources/filtered.properties @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# +# Not Replaced with the pom version +# +app.version=${node.version} \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-133/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-133/verify.bsh new file mode 100644 index 000000000..88989630b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-133/verify.bsh @@ -0,0 +1,66 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + + File webappDirectory = new File( target, "MWAR-133-1.0-SNAPSHOT" ); + if ( !webappDirectory.exists() || !webappDirectory.isDirectory() ) + { + System.err.println( "webappDirectory is missing or not a directory." ); + return false; + } + + File filtered = new File( webappDirectory, "filtered.properties" ); + if ( !filtered.exists() || filtered.isDirectory() ) + { + System.err.println( "filtered.properties file is missing or a directory." ); + return false; + } + System.out.println( " before reading filtered.properties" ); + String paramContent = FileUtils.fileRead( filtered ); + + + int indexOf = paramContent.indexOf( "app.version=${node.version}" ); + if ( indexOf < 0 ) + { + System.err.println( "filtered.properties was not filtered with the value of ${node.version}" ); + return false; + } + +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-139/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-139/pom.xml new file mode 100644 index 000000000..a51346a8d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-139/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + testwar + MWAR-139 + war + 1.0-SNAPSHOT + MWAR-139 Maven Webapp + http://maven.apache.org + + + + maven-war-plugin + @pom.version@ + + src/main/webapp + + + src/main/webresources + true + + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..6a8fa709e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webresources/filterme.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webresources/filterme.xml new file mode 100644 index 000000000..2365ca4c7 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-139/src/main/webresources/filterme.xml @@ -0,0 +1,23 @@ + + + ${foo.url} + hallo @@ hallo + start-${}-end + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-139/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-139/verify.bsh new file mode 100644 index 000000000..390c0be32 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-139/verify.bsh @@ -0,0 +1,72 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + + File webappDirectory = new File( target, "MWAR-139-1.0-SNAPSHOT" ); + if ( !webappDirectory.exists() || !webappDirectory.isDirectory() ) + { + System.err.println( "webappDirectory is missing or not a directory." ); + return false; + } + + File param = new File( webappDirectory, "filterme.xml" ); + if ( !param.exists() || param.isDirectory() ) + { + System.err.println( "filterme.xml file is missing or a directory." ); + return false; + } + + String paramContent = FileUtils.fileRead( param ); + + int indexOf = paramContent.indexOf( "hallo @@ hallo" ); + if ( indexOf < 0 ) + { + System.err.println( "filterme.xml does not contains hallo @@ hallo" ); + return false; + } + + indexOf = paramContent.indexOf( "start-${}-end" ); + if ( indexOf < 0 ) + { + System.err.println( "filterme.xml does not contains start-${}-end" ); + return false; + } + +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-143/invoker.properties new file mode 100644 index 000000000..3376b9e49 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-143/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean install diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-143/pom.xml new file mode 100644 index 000000000..c975e36d8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-143/pom.xml @@ -0,0 +1,58 @@ + + + + 4.0.0 + debug.war + MWAR-143 + pom + 1.0-SNAPSHOT + MWAR-143 Maven Webapp + http://maven.apache.org + + + + + maven-war-plugin + @pom.version@ + + + jpg + + + + + + + + war-common + war-filter-overlay + + + + + debug.war + common-overlay + 1.0-SNAPSHOT + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-143/verify.bsh new file mode 100644 index 000000000..45ed59e2f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-143/verify.bsh @@ -0,0 +1,124 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +filesAreIdentical( File expected, File current ) + throws IOException +{ + if ( expected.length() != current.length() ) + { + return false; + } + FileInputStream expectedIn = new FileInputStream( expected ); + FileInputStream currentIn = new FileInputStream( current ); + try + { + byte[] expectedBuffer = IOUtil.toByteArray( expectedIn ); + + byte[] currentBuffer = IOUtil.toByteArray( currentIn ); + if ( expectedBuffer.length != currentBuffer.length ) + { + return false; + } + for ( int i = 0,size = expectedBuffer.length; i + + + 4.0.0 + + debug.war + MWAR-143 + 1.0-SNAPSHOT + + common-overlay + war + + + + + org.apache.maven.plugins + maven-war-plugin + + + + properties + + + + + src/main/images + true + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/images/duke-beer.jpg b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/images/duke-beer.jpg new file mode 100755 index 0000000000000000000000000000000000000000..8b43aa0650ca2f32c3b88620dbc36211a6e302a8 GIT binary patch literal 246218 zcmdqJ2UHVZv@bjuBp^rxK|o4SKvWc@Nl(B+5g|4}=?aL5lo08VP!yzvCLjm|3n0=| zdJRniA_nOlLeNN0s1cIz^1t`p_rC92-&*gjx7JtAV8Vp03gIJsr)1Vy==%XyG3&X z9PCePw*0dgID0*Ck-qWB>!}y&k(al$hT;W4<-!FuZRHEvDr(Xy7qr#XwUss5HFD&! zh5e6x_HrQaUu!W0vyb)P%OX7gQX#NE0bKs4ZU4*VzyS^p_M79tzb*f>bl{&#{vQ?m zzjph#-T#-L`z?R~H>Vv44dOTn91!3D32^Lp08jwnIQXx1|7(NU-w%K|4|0LIc@FWi zcc?l79N++f4se1F9^~X?Zw+N{2RH=|3Z76l;5vHqKKLY3NF_Y^4Y$mt&#l6@eo|#s zANWM@96BZF&4XVug%YG`U{U%qnH(8$>2+U+|QmR5J~SvxtqJam2J_89fl z*Uvv7Fevib^Qh<-F|jGBFVoU9US(#zefK`UpzuS{$1fF?Rn;}Mb@grS9bY>MUESaM z2L^|RM@GlSNps}+U%x4T78Ysi8=G6(JM_Q1|LDa5fd0oR`&Z5WKY9tU^*X@G3E~9* zqZh{k|9=!0;5>LjnM=^%Cip(`=t-4uZlO!bZ$7v3$f(|;3P14qdFYs|8cB}!k81y- z+5etm5&uV;{SU?dr(WX#ABcl}@jwCq9AL3dzl{a{J%y3H4@3~r16ec6yioc7;o=yl7Kfuu1yFoWHbp2Fcn>F2IPM;t#6)&8kL89-UI0hG)7(Wzq{JV{tr88HVi}br$d=7pJTHD0>p-=_K+w}Lfqme)5-rF~ zp$%7sZ%>4@E4`@fa>n*tqvJ_a>L)5Q<`5Apr|$on5quEpZu+8oTOnrr_vHEb59#|r zy&7=ys?dKqu}Wfu(i`3|IO$3>4YJaU2-Eir4eG;&xuQ~g!xs*dZs~$Og$of^4V9~U z_kl4-KH+NSJ3oG9`zD7U84L-ny~~NbjH7gyJ=H%MmZilE2O69q$5M{f>95&_@lb_^ zcaFO>rr(Tb;%?IHdS9O_Y4xK&m^+{N`xgLzS$>3GY)e~IBKYQ<7dJj=kSz3pK(E;I#}=im&eaj5y-~Za&>a5{ zZ=?@#0XOoV>YkR2uJRfG;%V8nC9f%ILRsB)W&93<1x8!bYTBVY zte)pYhdn>dGg`T3aMSJq@x1VN6^@vb&PozLH6Q(swZdL?eo)Bdz|1D@15`^QC#Ll( zOI#ze(M~FD7B^l@Dtqp^4;&5Q5dL$Kb#|a6i*Wt*Ty(_!EVV#gb^i}`HlZH}1Iyn} zJr)@6Irq8SOBdgYcyaF770gt`q3ok0oIWZ61o*3RRjPj?OCC*x3pL8nDrWJqTMnQQ zW9qSw^@ONKqxauvy_^ll#f!zeKu-pr1{Z6|)ihSURMR-N0}=Z8tuqWh!XL|sWB2uI z`#`HDTAaN$VF?_pPc)?(t;kV#u2sg7A#Ih-M(3ziRnKrobcGLh;2t$mg|{}h_xz(% zP3xM?v=`l8FYj1k%3l7LyTPLwETa#4_)<)64MPaEosJoA*zTu)rGXL6DLXCGA|tCT z@sNvN+B_?#sJIBvGPQuMVv*-U>IKCgCT13II(x1A8Ylml*JQP0W{Vvv|1i_=5Ad-t z)qyp^kxqN{nnV(!eGC3b5FXp;<@UiwJkGD$C|-i5%V0`h%|IXjwegc6aoMkI)7dA*L%8ot(99=5K29E?LaP_M&Q+ zm^42VX+QEbQ#M$tZc}T8g|sy+b>=}~FCYMpkvCg&VY;tLkWPm<+?VK13^*i+E;i?m z<7OCArSe9y%-?bQe;F((a8S5S-EMMDIl0&jZK|9)JXG=Np1}H{`gcmZW|&q*_NY(* zKLnr6haNcJ=ub+kq-K02f(;7(>;vL3L9-8X)A_UIM|(UC2A>k1V$G~tH8u=Tr!vv^ z^%oO8E|?YvCS!UdJA$+k;t{Wi(u_R*yp4$urHC=yb~>H7ClC@yEnk69p>`feJ6Xrv zo>U!%`6pLG#RG2b-i6vwU1Lk$IjSE%u&(onxA;I`ugAebIYsHHE5JTr3!u6du7(`k z2RPYniHHh0&R!>kfcjxd43)H}aG?d-=z@bVOJsWHYG64-jaHH#r@ortuhK3sq~@CD z8me>i?gP^$XF<_7X%8cm@Q;AEv8Vrw$i_?@T(D7tiX!2#th4lH*T{A#r15-dK!r^} zMCFRp+tJ=&*<)R+Z&vdkrJsKJB~tbq?;(J71wT7GGk!PZI`bWaXHWf+b#xACCR|&K z29C|E8k7yb$2t)(-QWLPvx@gLu1928O4#(^vu_1=GdeV2HfL>Xz1qbsMq~5%_y8^> zL8@^dz?Hg%7|;{v5D7S8v>d~g_PpIL;Po5^+h|G-wd+p$>JES5>t1Sj{$j6O^7zd4 zimEJLQ{y;|RCgC4>7;CTkI<~Cx3{4$QS~i1D9NBR*l%}drz~jwNNbc9s77`V z5|TJvA2WDFbE^}XOAymDgr_~Qm1k}m>9wrIQ%1K}^rc*j#}v**dff5KaFf>wB4Z}>G}h?#(S zf=R$o5#{4x17;qY+abNVGTsfQLA}bH`Yt6#Pn#VpjP)>WYd#q(?D(-$E~S6@s_d|c za(>6z4qYEi!N{`0o;O37iEjo19LR*$ggfG)m$=V`c+3{>#ktZD?TF)YC8~=oKAJ@( z{}uo0b<@4q&w86y8jO6OtO-Rzf88%xcw_p;7U0(+*g85f&}o}H%b>mB5I1&T@d&|u zrW2`UZdHkVk9*wTg;>+?`3LLaqhWFZR~19XJQUyZ-6D2azVB7c_`p4GW8(6($A*aH z%Y!a%T@4x7lVJ@*!59xjIEzwI0Q4=Vb-I$}rFC9~8RzpI*R!x5_C97QZYrpAZgr#RIJG%~JT75Hs4y zSDrF@y|B-~7(t~wC&T^!V0Lk48 z*gBa+itQG&+FZZutiBe3)V+BsOxMO{pa$%Mc*U1^b0D`#nX%O4YYHq$kAwK)B?M9% ziohd(Sr~&wOUI~#=E#xwFeB+*TI@R0zN5I|U<<`BphcEkKER;qL+#kklQMobA9svB zyuHFpT6Io~+Ypa2glJ8}xcjsU($Q&MOW2A2lQ}XDN9j*ZUiLHm3&$&0Tzy1b+J5&h z>+%;UZ4NPWF^`mi<5iJ%Zay29LRxClds9{F}+N&8gc<4d*^YA+71CX|e({&urw zrj2HeulL`OiwO2N1V?$TD-Lc_Td8&5Qi66--BMM%G3SJ@rzGfd{ef-=%0k|FrFXIMy?Ioo35aUD`-th~Ku?w=ad7Q;FBNTRH70$PR5yoMHiOteJNU)0Rs{d7f^%9NIFel!iD)s>u*3SfJzy4 zrtK?r=nFx{)=l<=nS*(vD~>m=%ebz3eHH$5A83P}rxFuG&Xb7ZbXD5{T{99?NqMUQrP8sK)y}!j-#ibJd8|wfIN2RgVg-RP5hi3L85|6d zkyzJ5&__1q+1;J(Tu1ah#(dfbVmHgjT(?t(%hjP{@J{VY8XeIwg6h^AX~7(Gx9V24 zmw(;ROGgF^K@Wi?DO|0{OYZND)GY(h|fb$8+tr0UmsjK?|WFA z_2bd1htd-zN9^zZ)#165mcecw^9er((_Fk;W{1ZAT|udTPrysz8%#UIfr@W|M$QPr z)B=8(-*l-rV#h~&B4Ye77V!rqQ?jdcSd3Rz|FPaMJ^KQ!lMo?f1R5 z-yE!zUWwv;^j7~v?7`R0vwY6Da!G#Fsy<^Wx8I|&R>mwFNt9JI8o@@?2Lym5d^P!e zj5HT9koGK~HV1~Y57cG*(B?x*23S-#gCE}cDv$qF>wLB&MUOW8UXL60m-vZN4rO`{ za`5H0XGQLiYyQa%wL3BB~GFEZ~StF~4B&$EfD?0R(|wz4XE%g3a(L`5+3{8bH$hwxR|^~2yxO}#_Vo#FfU?SL73TUfA|4)uS2YM zTooavAY*p8=zMiFRer9yE@U0k_Gw5yOWZ@1v=-~7bV>&XHJ7+rXzSUTsD`wD^#gYy z7`n!T1V_UJB~W7ns&3_{YdSRaX17`oLyzMHR*_Byc7d6 zqQ)^iE^Tpx3rXi!4#(ms!_`iJ-+Y)R`Bw|$FB5b2%>j~?Or4g@B4i{RWo^vU+OpQQ z))()}9QfPEN&d70`lLz8#w43lBpkgV@?x{|Xnc9TwAex<)+mIj%nHmWegkBHRiaF; z$pm(4A2<$#GtEO)Ynn0kTbVF(A8$j=KPH?h95PrRo#E_EO+aFA-Urzic`ucgx{}K( zOkU{Py}@N0S!Se5me_lA=i;3OW{`YobkKaTSPHf;qZs9+9`r2 zIX5iSs0z4+E{vDTsmJ0GJ?kHD@d;|pOKG-rtr5&0?`^`v^n(1>`ip8oS`vQC+yt$O ztvtR3p`%0zzNf@OuOMQsx}&%DylZ%FV{VWWl=*nrS0c4rf}?ef;ALyUBNCb1mmTCd zAO07_j@{=)HP8AO@{9Ts_5qvCvdcHC`JpY5>!T$~ zS+LJ2vf;bPrJ~uY=Y!ZRQ~b^6DIN&$`9LmdGT~sl$9dCyN!%@Rfkha9R~k8Kn5=NbCav&d%{$ zV9}*Pob~V3l}^#+Ey*uIWX&ag=p=-3M1;lr1pY`I!iQ$}=c?!z{m?`yiJzS~YV;ho z2`v=j`t#{~<2-6vo&QrIXiR#wj&8c^`4Sr&RmG*z{GREB2ZpDx4e`NZePk)#HI4Fc>H~RRPNRmoZ>7>Suud<%cR2uKUj8I^*LA_@k~W=x{6*Gj6^Q%z0)~BRh8RV1`LSfax5c!`KpuiL&`w$qY8C zYRF=7-_pBnHt2FC>zkXbv4c~L)e6vYCEP3|a!(!8dIQZ7a;uUi62a1-50LhOt>~sF6mmU^0{Yi zOUKf_{S7Sj{k-dO)#6L@m%-BW<(o?lHha9IC+jPkzSKr7_OFgV6*NG4pg>=2^=V(e z)C-n~9x*Pqxbpn}4oPN%&ocan;ss{{oD5QW+KxMa zIV@I+jcA(l3$3s|s(CSh`TPfs*=^6;# zs~t)V_qa9fpFWI$E|$AtXb0v`XTH<$o}5^)eo~TmA(ZDWvi>Y1^>f(S(_**yjm$#> zh_?Gc?oe1(devO0yEV-%`ziCUtDcnR zy4vKa7}r1^`BU{vcc~B%?3GlZyv!sfyN~OcX&3;iaq5=N&JD$M7cXQx)p%Y2gBX}= zrN@(W$W%(+4};>z%{VNw6IfaZ*N2Ew3c=uJe&cSf*^PAP7pQ-suCCne9>&CK3{}{Iyc^EQ{9~myBRj-NfsDEBnAm z=^1Wdg9u}!4%C(`M1w{3lv^Yr68t&f%{)GeCl!_JOlNK~Hw%Od0nUMO4?q=ucpmG=CwuM(P#(TUl+lmRL7= zKsMXQNd4xZlYBe{t0^5Ej8VD<I1c% z{66}UG$Oi5+pnbIL$s{x8#0z6k0auOfY~)Rl!=@uG)OYhJvyQVP7EUX@`gT3QVx&j zbMm|ZP*S_#^CKVZFI{LC)uhB@g5SuO(??ExusK?r_D4q`Nq=ER4|5IJD1PC?(=sa& zpB*_dA*pcSH%Ani6VfufwKRf5r;SavC$tZI{-cL@LWM{cANqZQ2$Ii6hNmvz@&WIf z^oE!0gW=JtVO+@gL*M%vFVD?HqZO$$9nff&^pLI$xtz=WnEeTAP0Xf3=98*W5u1LB zX|d#`&fn)mf1A7mXGe)y@x>b~tvYfF8acJ-1^j@+f7uTGDwv%FyaU>@LlNk!0Oq|B zLjcY?v_Zi&my3`1$3|NFLX1~k}y4o^rFDnkv5$8WPA4xMmXfm!$0{?mx{a3 zTivdV3tFfd`S|*IQ}2iJ$whWdS03pghCYM-juQSSp_fq2@)Bs!-EM;9lO3G=@-N0-oKx;F-?LDo<03G z?9hK8f$^~;+H{5~;yLc~PDjq;-@}V$O7=}uOt^28`A?IKc5C^BMvMG_U<;bjEAXz; z=#w?s9M^BJWUpk}*$GFJ2rINQwx_GZI>P!keB8f%N~TR^Nkj@7>mIk~_1P@Y7bO?l zSYsXaMsYz&$r>f2qkQgbjJ!HiE5wm*HtT?$;nVe{2G4oM=SovWI=l`qqJ?SZNdsn2 zc7iO6K$p+dG}QQ}^sj3u_i%Q#&Sh~XoBtAKII;R1s74r0_JPkcP$c}g&Rh+leeWKt z7bZ=M%BND=Fr3Y5bjf93hp76pI?{|_KwNvXbUn@dHJN9*SvC}_bhyWD-t_H4$?q*i zQRDl*si{XzdeYq(dfE+=LZ?!H1!?K+h7d3B1Ccl39?%%5sIJT2V^+^jR#eTLQUnxS ziiR?-&~4P0Q>WeV5T!=df&tjJIHOSXR7b);XpN;hIHz$ zsrd_I@3UN{bL-O!syoMh0|su@#*j|l>e;h_FI(+-qKCSsPcbXtZip9BGX3frB19^# zbvOOZEF$Wnf>MV3wtE1g-O7);bS(K*Qr;kJ_*=`DBr~hw6WofyMK%u+pR)@e?R9V2 z_`#pTVC?g00Cysey|rQL%#9GdI2T}Y1KF2-XG@PW=*69VQ=;(;ZYYx1;ryX#oHv#9 z($0wcL^$WF7^8unvk!oeqr0Ygn=j__(T(;lvq$1Rc$RvpLK{n*ihWt(%Eco?D~~<5 zX2@kuUw(fHeAq!L;Pp0$Za^Klo!JNwJ9V%IFBSq8SnUlUQPl{ppHjpM)$4cjEo&Y-xaQ*w`hU3Ue^m6nb z2Hg#>K}4-KUfxq>I8kR3%hVd~D(PM&K~>D4gl6H0ImF2`AttlE77y_KD_NMSk`-Q? zN?e0U=6V&&8z&AEakIRw_i^#!$*u7l=})@6>;k90OkZ?;iKX9+rR%igk2I^Yy76MV za%=-+6~BdfK66y}aFP0U_?(Riykf3C&Rb*`R_q#ZQ{97WeN;^&9rdmq$@g3m!RcjQ zu41KIjjAi$|AO zay3#$3|R->w?j&n4gz%l+v~j@$B>mH!{=qwQ8CZX_#^isaT^4w8A^YR^09wIk?{$5 zx@N_8mDdZX2pY;Fu!ILWbH(X~=UFEhMmDCI7ws9BXk3}<m*}IJ;v8mA`PQPM;skv1E-;Wf=F7>h8*2 zn4Ec>=;khd?pB(6-n0#wo$6BE2gK<6z*pRH*eUwj+*+cS^H7KrP3G(PNPOevK$!~A zhozd0>29wllHE~lQ*zkpacRHH2P=`%+_3znyMhWoi|uX}e3o|H1^88yvHH7?=$9N8Eu3LM zH=`Sr8o3x+to>v*u|+DBjm%o#a$3}vr4&AK_WPr7(W~RBUeJ2L?B-n9X_HQUN&GI8 zcpXBqaN(p(=ppWB`|tYh14~2QV6Y@ziaTPLNdm2sS}WMG?T?A6wT?-uM19mmxjj-}VrbAv z9-G;^(@maXV`XlmRN;5c;@V`2QU>rvAJo+_DZeE=~Z!X0NOaI@tMA(8E1Y9Rq1soiLH z?46g#eKPK#-DDf<6y2R1uuQ_;qx#2qIBA)_kWXe2eS~agY@XCtKa8R8UBY+Quy`u= zfrCfrHEeXdoB8hPOk6Wa1bvc@oYVN+*Q^$D&b>O?j5ZT#x#FC*^6c4Hm$|)z>2slH zH{%Hkje@BjNNwDf1T$+NaDzT`s48sr0)+(Ap>xG>kq|xFYU;6f1=ADSyuBBxXUppGWR|wk zqEvWd@oOy3f$lQP`zpmjB%tSJ)Wq$djXW7VG1YcWo++ zT)B$XAZn|mfe^kj z>h#9wERUKU%%ED`xhd!1grPJui}`vBelSFbYM$ZhoNnpeen48_@6B`Wg6C|GijKd_ z1xr|Q$qRV-@lU(j9Y0*=+At=$vplhPlp)A`-KdcN2gXZHPj;6}$Uc%e_a)-gDb1G2 z??r4tC2PmwxINvqecZSA_dC7x95*k{M74@;iDT5Sf}V=2^h0k9DAii!$<11rRs*zX z$Z2r<_!TRui}biqSCsF2x{^We*jm>3lu3rgu;;3rv94a;6@66)dlTWt%f-jqaFk-Q z*UC?t>GpeC8rzwLN&aEyh@lL}5g!{UfWaO=`HuTG* zk3T;hnL0Ba`1{?>tCu9IrOyRBW(^fs;SQZkk#_F>_ zOK<-;vw-J)3K3?w;#2OuT+~*_7E*~M{@jx9uc{hf_YOC@9N*5tU60l0uULI-1 z(Pj661eW4sOSk~WeMbB-Tu^<&cxB=ewL!n$D?H@HlgRJUrquFh%lDpamTJqj$$Gct z9Td|luMM%ZNkMG2LFaMGj z$#Q)XVM0`=`gb5k3FZ~1QG;IUX4We?zeKE`PkkN8(5~Y6O3{ zbwc8&YFWF0 z@JnVTOc_0fr(VbLJf4yC4K@9WIfCX5Aa}qIo}zVD=zX28B10f_>k-pB>hPQL$0GMa zZ`;-M`*J^7^~{_$P5dNVQ8B6Z1x^j7;Yn9Cj#U&J?5(T^y1K5(fcy-VH;Ouy2VDoP z{@@tmf7y2PKAdU>#nuy+*Jyh;)1hNfG9~7`%WL~upHGZqbewme+pxWc59Ki(#u__W zIZXb0-B7{q+C}KYx+qHh(fXn6l!V;Zs!ZiSABX*`>!0VBG7~y9(ygkgQ)5#T#;G=T z){~tTHj@**VhZOVM~h|7oJb1?b6RBeg}sfPL(4KQQw!Poah3}GH95yz585ULqE463 zl?yz+bzpAf+Ru_-P4&(RfmwdW)mM)*`b|#0jre{V^w*Ca*Cb)wpQlWaGL|h!3S*XY ztLyD^l&^%E^ne|=>bOAHStzf0jI?Q-o7a5vkfOj)ScdU{B=?D>zv%w+SCEB0BW^^_ zc@C@=F=%Zccm&1TpJjf;JaS;CPAkFj;EW=M2@UMYC*pMjx>VkQtrEa;Bm_{NI9iXpk)!0m|oEb8;&7UHnt|zn~QhTz1N})W`{tB>uAHe3yi4@$*c39r;oYz?qB;B!@Nw389;;eY?$A?ef!5sh1?HhXJ~~4+c4IAU{73@l zsXVg&F`;kXf2Wg!{DvIK_UlZ5g~dAga(UhOA^A>(gj6o2IuvHRxmEmwn^ML`bc|cb z@aTXq|(s)yk?X!`?s? zULMR&L3r7#uH4-@lZ`d^L2$Y*Boa2Zz5+fP$x%OyZs>6Z%u4L+uq?XlC&;p4wO}m2 zIYQr+@(^Ji!M_PWn0P?5Jukf5b*No{hp6OYJfE@jV#YITNcE{%Vt zyod;*@~n(TVj&x49|QKBM&gfR2}^^P>K*oze!6=c6S;*;vA{3Slaq*V%1eC*c;L}N zE%jl7p%;^rv3XKsTT+c9PP$rMGN9}&F?n4_f!v{JBp8f+m(9;_Xk;E0rg{ESudRxT z^AR5;E@aQ2oul1CvAEec`$1>g4C5#-Ip}UcuAS3Y+!tI&@3rZHIbxV}+Nvvc===Bd zZFW#6;4a&yscZCr^WpmBeRWgO`D%|01$QL)xA1qdbAa9jZHhD&A{D)`w$%05QsL|c zg`^;7DSiZ-CGaf_J_aWd*%pgoO%;i3mJiXQNoCIM103|d_AEr4uFRar@!vX4sO&Vvisn84Kl#7P9N_aRgNY91I{YN0;mjCu*@pCe3epCJ8W)nv< zT7o{>EJ20NrFPC%9Tvfj!2a_WM}D&ERZS;EVBx38V(QQ)BNUG^eGnU9fBaX3npDS`0RMXjhzU zw9->R7a?t}M1{`<=xR9?q<^A8I(R=sz43ZKZJeLc-YoDl<*L`QfN={#nw$8!i+j&% zDh$KeGk)OS=srt(K5G82p&{n4^p~eO)gO-Lxqc~ng`5;B!Q5o9e9lIOP{w}s(Qr?HH@CRoyla<3=L+D;Z86}sjg_ySdP87vl_L{=8*7{gf~>8Lr4XC>{%F7fp)&7NWzxq;Hb+fdt)1&xll zB?}Gig`>h&cLKfzU7*At?VDx!{$<}@UGL15^HVQCa)J4?5wL6 zdw5hw|JvzPdOp1Sv$1aAl4g8e%Ee1xo`~IG_{*o_T1I}&bV4F8Ju189eqDKzXT8;M zc3$IwiTrx`_!KfrC)1u})vePdI~&rGasd?{zezA~fYZsX$Br)_og9B3;P{KI88Dh; zvXD|5FM(>?F29!kZOxTs#Fx@w*|*oj;x=Mi!JY=@tHkH<`!f^49W@9l?1Vd;cn%fc zD$dAc38~r=JPH_kH0IPmIwl&%rd3WE;~B#* zGvlqn_c1QIR+j$N{x{EUk6310P?h`In5658eO6o?b<3a^lcLcv^IYI_t;3hLMbmf% z>H8ZJREAVA)ZBgZHjAf_-K1(}0h>XVl~~9)M|&QXzh{8%*@8Ol?SKtLis6tu{-|eRn!CR&r%{RImae67Rk5(FO-cU8r1c+VLX1pfC@vqz z#r}UaoNDfnFvFw17Mm;n4-`P!!|~D)H))nlR3af`n2nR>xG`V5>3ttRpBUv^{9+#< zofyisK?(5`^aP|de*V%-u!OhGtUzd#uk2K_*9s3!hdh%ojw)ps((Ja23m?(C&r_*v zZ(s1X7YWC^@W#<9cf9>d?2oZ~4=mH(22Nc`(YukT_4C{_Wy3efwg__rGFX<0`Jqq~ zu9C;m0vtiMP!bD5LovFT9BdceKBME2Sm>1dkvpqzqkJZ)z-~Domue8*6cnIll9~E zEM9VSf^PKn_{%u=Lib}k28~)J_6;5pWBm)y>aQ+`y?t9@#+Pz{^Yby|#Zt01K;FL52SYXn*#0DU%L@G|Q}aOBndFf^->%KwP4#m=AiG2Utp&{iZl!|bQ z!55F}amrii-lbXD2BSuFgZ#8Mzgj$r&)(6c6lxJa&$8}t7<2=v1SDY&;&6Slq3DzLCl(WP*DL+rNDa^hAQ5wGoSP@9mBc{Gm=IjRMoZR!vIR; zNQAlmyue5_uhxjqw5Gq+oX5S&Q&jc+A$2!z&{-I~jYyNHt4+}xLIVkn?cZH9Sq4K}xY(4)~ zQIekV*|sxLDEUjhHrFj~t}ys`{8J&T4JS58$j-V&d>=zk;rTr<54{rWr>bx(zSPwA zq>qbMJ4bA~X%j8uS4=&d)%!_0A!{OI)+M~~xhhgL4q$IzvC}727PwtI(^^3aw0xU}({-+wJSbHz#uZuMBuF+8U{c<&gFW5PB;2BaCp~yn?4~Ng7%cQ%CCkN4L472_<-H>xP*Fy9-a}W0^ z5+WZAX+%|34Sy}ENtswt@SX-?FWs2{22O7%_)6Hs)lagl_ko8C=*2dfctj#&3dsE==fOb)M9hq!VY|clDSVELnBdn}^38 zY5Ca)?_RtVJ+3?w85zHOXs(F&uT0s?lJsiVO$tfR)O^ITGGgT0i=xDxMo>ayn9k

Ktjiw$W@LM|6D^Ah2)JR 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 L{ND|^A0z(&?{OIg literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/resources/filter.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/resources/filter.properties new file mode 100644 index 000000000..624f14f21 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/resources/filter.properties @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +title.main=Prototype ${pom.name} +title.version=version ${pom.version} + +debug.test = ${my.filter.value} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..357cf72ca --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-common/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/pom.xml new file mode 100644 index 000000000..062fb1b80 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + debug.war + MWAR-143 + 1.0-SNAPSHOT + + custom-manifest + war + + + + + debug.war + common-overlay + 1.0-SNAPSHOT + war + runtime + + + + + + + org.apache.maven.plugins + maven-war-plugin + + + + debug.war + common-overlay + + true + + + + + + + + + hello world ! + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..fc6e240ce --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-143/war-filter-overlay/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-167/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-167/invoker.properties new file mode 100644 index 000000000..7e6fbfd9f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-167/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean war:exploded diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-167/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-167/pom.xml new file mode 100644 index 000000000..174386f45 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-167/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + testwar + MWAR-167 + war + 1.0-SNAPSHOT + MWAR-167 Maven Webapp + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + + + + maven-war-plugin + @project.version@ + + + src/main/resources/MANIFEST.MF + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-167/src/main/resources/MANIFEST.MF b/Java-base/maven-war-plugin/src/src/it/MWAR-167/src/main/resources/MANIFEST.MF new file mode 100644 index 000000000..285550382 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-167/src/main/resources/MANIFEST.MF @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +Bundle-Name: Dummy Bundle +Bundle-SymbolicName: dummy.bundle +Bundle-ManifestVersion: 2 +Bundle-Version: 1.0.0.SNAPSHOT \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-167/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-167/verify.bsh new file mode 100644 index 000000000..fa82b4309 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-167/verify.bsh @@ -0,0 +1,74 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File manifest = new File( basedir, "target/MWAR-167-1.0-SNAPSHOT/META-INF/MANIFEST.MF" ); + if ( !manifest.exists() || !manifest.isFile() ) + { + System.err.println( "Manifest is missing!"); + return false; + } + + + FileInputStream fis = new FileInputStream ( manifest ); + String manifestContent = IOUtil.toString ( fis ); + + int indexOf = manifestContent.indexOf("Manifest-Version: 1.0" ); + if ( indexOf < 0) + { + System.err.println( "Manifest-Version header not found" ); + return false; + } + + indexOf = manifestContent.indexOf("Bundle-Name: Dummy Bundle" ); + if ( indexOf < 0) + { + System.err.println( "Bundle-Name header not found" ); + return false; + } + + indexOf = manifestContent.indexOf("Bundle-SymbolicName: dummy.bundle" ); + if ( indexOf < 0) + { + System.err.println( "Bundle-SymbolicName: 2" ); + return false; + } + + indexOf = manifestContent.indexOf("Bundle-Version: 1.0.0.SNAPSHOT" ); + if ( indexOf < 0) + { + System.err.println( "Bundle-Version header not found" ); + return false; + } + +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-240/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-240/invoker.properties new file mode 100644 index 000000000..c743aa4f3 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-240/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean package diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-240/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-240/pom.xml new file mode 100644 index 000000000..5ac2c0d99 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-240/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + test + mwar-240-test-case + 1.0 + MWAR-240 Test case + war + Test project for reproducing an issue with classes packaging: https://issues.apache.org/jira/browse/MWAR-240 + + + + maven-war-plugin + @pom.version@ + + false + true + true + + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-240/src/main/java/org/apache/maven/plugin/war/it/Dummy.java b/Java-base/maven-war-plugin/src/src/it/MWAR-240/src/main/java/org/apache/maven/plugin/war/it/Dummy.java new file mode 100644 index 000000000..e980d3552 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-240/src/main/java/org/apache/maven/plugin/war/it/Dummy.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.maven.plugin.war.it; + +/** + * A dummy class to show the problem with classes packaging. + * + * @author Sergiy Shyrkov + */ +public class Dummy { + +} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-240/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-240/verify.bsh new file mode 100644 index 000000000..fec4b9d43 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-240/verify.bsh @@ -0,0 +1,59 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.regex.*; + +try +{ + File jarFile = new File( basedir, "target/mwar-240-test-case-1.0-classes.jar" ); + System.out.println( "Checking for existence of " + jarFile ); + if ( !jarFile.isFile() ) + { + System.out.println( "FAILURE!" ); + return false; + } + + JarFile jar = new JarFile( jarFile ); + + String[] includedEntries = { + "org/apache/maven/plugin/war/it/Dummy.class", + }; + for ( String included : includedEntries ) + { + System.out.println( "Checking for existence of " + included ); + if ( jar.getEntry( included ) == null ) + { + System.out.println( "FAILURE!" ); + return false; + } + } + + jar.close(); +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-306/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-306/pom.xml new file mode 100644 index 000000000..facc3b537 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-306/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + foo.bar + MWAR-306 + 0.0.1-SNAPSHOT + war + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + false + + + src/main/webapp + true + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-306/src/main/webapp/index.html b/Java-base/maven-war-plugin/src/src/it/MWAR-306/src/main/webapp/index.html new file mode 100644 index 000000000..873f9bdaf --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-306/src/main/webapp/index.html @@ -0,0 +1,26 @@ + + + + +${project.artifactId} + +${project.artifactId} @ ${project.version} +${project.name} + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-306/verify.groovy b/Java-base/maven-war-plugin/src/src/it/MWAR-306/verify.groovy new file mode 100644 index 000000000..ba74120d9 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-306/verify.groovy @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +def indexHtml = new File(basedir, 'target/MWAR-306-0.0.1-SNAPSHOT/index.html'); +assert indexHtml.exists() + +assert indexHtml.text.contains('MWAR-306 @ 0.0.1-SNAPSHOT') diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-311/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-311/pom.xml new file mode 100644 index 000000000..820173b11 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-311/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + com.ecbrodie + MWAR-311 + 0.0.1-SNAPSHOT + Maven War Plugin Bug + Filtering of properties files (as a web resource) broken in maven-war-plugin version 2.4. + war + + + UTF-8 + foo foo + + + + + src/main/resources/default.properties + + + + src/main/resources + true + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + + src/main/resources + true + + **/app.properties + + + + true + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/app.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/app.properties new file mode 100644 index 000000000..057e159b7 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/app.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +my.maven.property=${my.maven.property} +prop.a=${prop.a} +prop.b=${prop.b} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/default.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/default.properties new file mode 100644 index 000000000..b16902f2c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/resources/default.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +prop.a=AAA +prop.b=BBB diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..b457706f5 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-311/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-311/verify.groovy b/Java-base/maven-war-plugin/src/src/it/MWAR-311/verify.groovy new file mode 100644 index 000000000..77599894f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-311/verify.groovy @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +def propertiesFile = new File(basedir, 'target/MWAR-311-0.0.1-SNAPSHOT/app.properties'); +assert propertiesFile.exists() + +assert propertiesFile.text.contains('my.maven.property=foo foo'); +assert propertiesFile.text.contains('prop.a=AAA'); +assert propertiesFile.text.contains('prop.b=BBB'); diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-314/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-314/invoker.properties new file mode 100644 index 000000000..692791cf1 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-314/invoker.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean package +invoker.debug = true diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-314/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-314/pom.xml new file mode 100644 index 000000000..da9ef170c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-314/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + mwar314 + mwar314 + 0.0.1-SNAPSHOT + war + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + false + src/main/webapp/WEB-INF/web.xml + + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-314/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/MWAR-314/src/main/webapp/index.jsp new file mode 100644 index 000000000..41db44eef --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-314/src/main/webapp/index.jsp @@ -0,0 +1,20 @@ +<%-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --%> + +<% response.sendRedirect( request.getContextPath() + "/groupSummary.action" ); %> diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-326/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-326/invoker.properties new file mode 100644 index 000000000..5e07ba581 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-326/invoker.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.description = This test only checks if using extensions works without any issue. +invoker.goals = clean package diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-326/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-326/pom.xml new file mode 100755 index 000000000..37f123963 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-326/pom.xml @@ -0,0 +1,46 @@ + + + + + 4.0.0 + org.apache.maven.plugins + maven-war-plugin-test-mwar-326 + 1.0-SNAPSHOT + war + Maven Integration Test :: MWAR-326 + MWAR-326 integration test + + + + false + org.apache.maven.plugins + maven-war-plugin + @project.version@ + true + + false + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-326/src/main/java/A.java b/Java-base/maven-war-plugin/src/src/it/MWAR-326/src/main/java/A.java new file mode 100755 index 000000000..474b0a65b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-326/src/main/java/A.java @@ -0,0 +1,25 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public class A { + public static void main(String[] args) { + + } +} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-326/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-326/verify.bsh new file mode 100644 index 000000000..c0a4425de --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-326/verify.bsh @@ -0,0 +1,52 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + + File artifact = new File( target, "maven-war-plugin-test-mwar-326-1.0-SNAPSHOT.war" ); + if ( !artifact.exists() ) + { + System.err.println( "default artifact should exist." ); + return false; + } + + return true; +} +catch( Throwable e ) +{ + e.printStackTrace(); + return false; +} + +return false; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-350/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-350/pom.xml new file mode 100644 index 000000000..6ef40f407 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-350/pom.xml @@ -0,0 +1,69 @@ + + + + + + 4.0.0 + + org.apache.maven.its.war + maven-it-mwar350 + 1.0 + war + + Maven Integration Test :: MWAR-350 + Test to skip WAR generation + + + UTF-8 + + + + + javax.servlet + servlet-api + 2.4 + provided + + + commons-logging + commons-logging + 1.0.3 + + + junit + junit + 3.8.2 + test + + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + true + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/java/org/apache/maven/it0016/Person.java b/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/java/org/apache/maven/it0016/Person.java new file mode 100644 index 000000000..4a287e77c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/java/org/apache/maven/it0016/Person.java @@ -0,0 +1,35 @@ +package org.apache.maven.it0016; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public class Person +{ + private String name; + + public void setName( String name ) + { + this.name = name; + } + + public String getName() + { + return name; + } +} diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..c1af95de8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/index.html b/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/index.html new file mode 100644 index 000000000..8e81e877d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-350/src/main/webapp/index.html @@ -0,0 +1,23 @@ + + + + Hello World + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-350/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-350/verify.bsh new file mode 100644 index 000000000..9e5c87990 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-350/verify.bsh @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.regex.*; + +try +{ + File explodedDir = new File( basedir, "target/maven-it-mwar350-1.0" ); + System.out.println( "Checking for existence of exploded directory " + explodedDir ); + if ( explodedDir.isDirectory() ) + { + System.out.println( "FAILURE! The directory " + explodedDir + " does exist." ); + return false; + } + + + File warFile = new File( basedir, "target/maven-it-mwar350-1.0.war" ); + System.out.println( "Checking for existence of " + warFile ); + if ( warFile.isFile() ) + { + System.out.println( "FAILURE! The generated file should have not be there." ); + return false; + } + +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/pom.xml new file mode 100644 index 000000000..12905e211 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/pom.xml @@ -0,0 +1,46 @@ + + + + + + 4.0.0 + + org.apache.maven.plugins.war.its + mwar-352 + 1.0-SNAPSHOT + war + + + UTF-8 + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + src/main/webconfig/release/web.xml + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..d8f4f21de --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webconfig/release/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webconfig/release/web.xml new file mode 100644 index 000000000..a7b8744cf --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/src/main/webconfig/release/web.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/verify.groovy b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/verify.groovy new file mode 100644 index 000000000..753bf8465 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-352_custom-webXml/verify.groovy @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +def warFile = new java.util.jar.JarFile( new File(basedir,"target/mwar-352-1.0-SNAPSHOT.war"), false) +def webXml = warFile.getEntry('WEB-INF/web.xml') +assert webXml != null +assert warFile.getInputStream( webXml).text.contains('from src/main/webconfig/release/web.xml') diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/pom.xml new file mode 100644 index 000000000..b2aa1976a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + mwar371 + mwar371 + 1.0-SNAPSHOT + + custom + war + + + mwar371 + generic + 1.0-SNAPSHOT + war + + + + + + + maven-war-plugin + + + + src/main/custom + + a1.txt + + x/ + + + src/main/custom + + a2.txt + + x + + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a1.txt b/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a1.txt new file mode 100644 index 000000000..7fe810a0c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a1.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +i'm custom \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a2.txt b/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a2.txt new file mode 100644 index 000000000..7fe810a0c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/custom/src/main/custom/a2.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +i'm custom \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/pom.xml new file mode 100644 index 000000000..beeef48a2 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/pom.xml @@ -0,0 +1,29 @@ + + + + 4.0.0 + + mwar371 + mwar371 + 1.0-SNAPSHOT + + generic + war + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a1.txt b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a1.txt new file mode 100644 index 000000000..fc4dee13c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a1.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +I'm generic \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a2.txt b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a2.txt new file mode 100644 index 000000000..fc4dee13c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a2.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +I'm generic \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a3.txt b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a3.txt new file mode 100644 index 000000000..fc4dee13c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/generic/src/main/webapp/x/a3.txt @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +I'm generic \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-371/pom.xml new file mode 100644 index 000000000..1f72884fb --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + mwar371 + mwar371 + 1.0-SNAPSHOT + pom + Maven Integration Test :: MWAR-371 + MWAR-371 integration test + + + UTF-8 + + + + generic + custom + + + + + + maven-war-plugin + @project.version@ + + false + + + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-371/verify.groovy b/Java-base/maven-war-plugin/src/src/it/MWAR-371/verify.groovy new file mode 100644 index 000000000..e66bcd5ec --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-371/verify.groovy @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +boolean checkFile( String fileName, String artifact, String module ) +{ + def customA1 = new File( basedir, "${artifact}/target/${artifact}-1.0-SNAPSHOT/x/${fileName}" ) + if ( ! customA1.exists() ) + { + System.err.println( "${artifact}/target/${artifact}-1.0-SNAPSHOT/x/${fileName} does not exist." ) + return false + } + if ( ! customA1.text.contains( module ) ) + { + System.err.println( "${artifact}/target/${artifact}-1.0-SNAPSHOT/x/${fileName} is not ${module}." ) + return false + } + return true +} + +boolean checkFile( String fileName, String module ) +{ + return checkFile( fileName, module, module ) +} + +try { + if ( ! checkFile( "a1.txt", "custom" ) ) + { + return false + } + if ( ! checkFile( "a2.txt", "custom" ) ) + { + return false + } + if ( ! checkFile( "a3.txt", "custom", "generic" ) ) + { + return false + } + if ( ! checkFile( "a1.txt", "generic" ) ) + { + return false + } + if ( ! checkFile( "a2.txt", "generic" ) ) + { + return false + } + if ( ! checkFile( "a3.txt", "generic" ) ) + { + return false + } +} +catch ( Throwable e ) +{ + e.printStackTrace() + return false +} + +return true + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/invoker.properties new file mode 100644 index 000000000..f2a7dfb44 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.buildResult = failure diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/pom.xml new file mode 100644 index 000000000..5fa6d853f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-396_no-servlet30/pom.xml @@ -0,0 +1,66 @@ + + + + + + 4.0.0 + + org.apache.maven.its.war + maven-it-mwar396 + 1.0 + war + + Maven Integration Test :: MWAR-396 + Test that a web.xml is required when project does not depend on Servlet 3.0 or newer + + + UTF-8 + + + + + javax.servlet + servlet-api + 2.4 + provided + + + commons-logging + commons-logging + 1.0.3 + + + junit + junit + 3.8.2 + test + + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/pom.xml new file mode 100644 index 000000000..2244d1599 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/pom.xml @@ -0,0 +1,66 @@ + + + + + + 4.0.0 + + org.apache.maven.its.war + maven-it-mwar396_servlet30 + 1.0 + war + + Maven Integration Test :: MWAR-396 + Test that no web.xml is required when project depends on Servlet 3.0 or newer + + + UTF-8 + + + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + commons-logging + commons-logging + 1.0.3 + + + junit + junit + 3.8.2 + test + + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/verify.bsh new file mode 100644 index 000000000..402a3ca27 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-396_servlet30/verify.bsh @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.regex.*; + +try +{ + File explodedDir = new File( basedir, "target/maven-it-mwar396_servlet30-1.0" ); + System.out.println( "Checking for existence of exploded directory " + explodedDir ); + if ( !explodedDir.exists() ) + { + System.out.println( "FAILURE! The directory " + explodedDir + " does not exist." ); + return false; + } + + File webInfFile = new File( explodedDir, "WEB-INF/web.xml" ); + if ( webInfFile.exists() ) + { + System.err.println( "FAILURE! The file web.xml should not be present." ); + return false; + } + +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/invoker.properties new file mode 100644 index 000000000..ea3a6224b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/invoker.properties @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals.1=package +invoker.goals.2=groovy:execute +invoker.goals.3=package diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/pom.xml new file mode 100644 index 000000000..a0d714624 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/pom.xml @@ -0,0 +1,79 @@ + + + + + + 4.0.0 + + org.apache.maven.its.war + mwar427 + 1.0-SNAPSHOT + war + + + UTF-8 + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + true + + + + org.codehaus.gmaven + groovy-maven-plugin + 2.0 + + + def fileToModify = new File(project.basedir, 'pom.xml') + processFileInplace(fileToModify) { text -> + text.replaceAll(/1.4.6/,'1.4.5') + } + def processFileInplace(file, Closure processText) { + file.write(processText(file.text)) + } + + new File('src/main/webapp/root.html').renameTo 'src/main/webapp/index.html' + + + + + + + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + org.codehaus.plexus + plexus-utils + 1.4.6 + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/resources/resource.txt b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/resources/resource.txt new file mode 100644 index 000000000..13a83393a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/resources/resource.txt @@ -0,0 +1,16 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/webapp/index.html b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/webapp/index.html new file mode 100644 index 000000000..8809bcf35 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/src/main/webapp/index.html @@ -0,0 +1,23 @@ + + + + Hello World + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/verify.groovy b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/verify.groovy new file mode 100644 index 000000000..5fdf4744d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-427_update-without-clean/verify.groovy @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +def warFile = new java.util.jar.JarFile( new File(basedir,"target/mwar427-1.0-SNAPSHOT.war"), false) +assert warFile.getEntry('WEB-INF/lib/plexus-utils-1.4.5.jar') != null +assert warFile.getEntry('WEB-INF/lib/mwar427-1.0-SNAPSHOT.jar') != null +assert warFile.getEntry('index.html') != null + +assert warFile.getEntry('WEB-INF/lib/plexus-utils-1.4.6.jar') == null +assert warFile.getEntry('root.html') == null \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/invoker.properties new file mode 100644 index 000000000..9695a95b5 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/invoker.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.java.version = 1.8+ + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/pom.xml new file mode 100644 index 000000000..8f773b1cb --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/pom.xml @@ -0,0 +1,66 @@ + + + + + + 4.0.0 + + org.apache.maven.its.war + maven-it-mwar-430 + 1.0 + war + + Maven Integration Test :: MWAR-430 + Test that no web.xml is required when project depends on Jakarta Servlet 5.0 or newer + + + UTF-8 + + + + + jakarta.servlet + jakarta.servlet-api + 5.0.0-M1 + provided + + + commons-logging + commons-logging + 1.0.3 + + + junit + junit + 3.8.2 + test + + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/verify.bsh new file mode 100644 index 000000000..3c7e8b5e1 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-430_jakarta-servlet/verify.bsh @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.regex.*; + +try +{ + File explodedDir = new File( basedir, "target/maven-it-mwar-430-1.0" ); + System.out.println( "Checking for existence of exploded directory " + explodedDir ); + if ( !explodedDir.exists() ) + { + System.out.println( "FAILURE! The directory " + explodedDir + " does not exist." ); + return false; + } + + File webInfFile = new File( explodedDir, "WEB-INF/web.xml" ); + if ( webInfFile.exists() ) + { + System.err.println( "FAILURE! The file web.xml should not be present." ); + return false; + } + +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-62/invoker.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-62/invoker.properties new file mode 100644 index 000000000..7e6fbfd9f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-62/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean war:exploded diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-62/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-62/pom.xml new file mode 100644 index 000000000..9c97e29bf --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-62/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + testwar + MWAR-62 + war + 1.0-SNAPSHOT + MWAR-62 Maven Webapp + http://maven.apache.org + + + junit + junit + 3.8.1 + test + + + + + + maven-war-plugin + + @pom.version@ + + ${project.build.directory}/webAppDirectory + src/main/webapp + **/*dev.properties,**/*test.properties + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/dev.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/dev.properties new file mode 100644 index 000000000..eb7ffe692 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/dev.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +foo dev \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/test.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/test.properties new file mode 100644 index 000000000..eb7ffe692 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/test.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +foo dev \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/web.xml new file mode 100644 index 000000000..6a8fa709e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-62/src/main/webapp/web.xml @@ -0,0 +1,24 @@ + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-62/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-62/verify.bsh new file mode 100644 index 000000000..0eeb6d9b1 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-62/verify.bsh @@ -0,0 +1,61 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + File webAppDirectory = new File( target, "webAppDirectory" ); + if ( !webAppDirectory.exists() || !webAppDirectory.isDirectory() ) + { + System.err.println( "webAppDirectory is missing or a not directory." ); + return false; + } + File devProperties = new File( webAppDirectory, "dev.properties" ); + if ( devProperties.exists() ) + { + System.err.println( "dev.properties has not been excluded." ); + return false; + } + + File testProperties = new File( webAppDirectory, "test.properties" ); + if ( testProperties.exists() ) + { + System.err.println( "test.properties has not been excluded." ); + return false; + } + +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-96/pom.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-96/pom.xml new file mode 100644 index 000000000..3a303a5e8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-96/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + testwar + MWAR-96 + war + 1.0-SNAPSHOT + MWAR-96 MWAR-144 Tests + http://maven.apache.org + + + + maven-war-plugin + @pom.version@ + + src/main/webapp + + src/main/filters/filter.properties + + + + true + src/main/webapp + + index.jsp + + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/filters/filter.properties b/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/filters/filter.properties new file mode 100644 index 000000000..0b24f47c7 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/filters/filter.properties @@ -0,0 +1,19 @@ +#/* +# * Licensed to the Apache Software Foundation (ASF) under one +# * or more contributor license agreements. See the NOTICE file +# * distributed with this work for additional information +# * regarding copyright ownership. The ASF licenses this file +# * to you under the Apache License, Version 2.0 (the +# * "License"); you may not use this file except in compliance +# * with the License. You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, +# * software distributed under the License is distributed on an +# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# * KIND, either express or implied. See the License for the +# * specific language governing permissions and limitations +# * under the License. +# */ +warPluginFilterConfigurationProperty=okitworks \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..6a8fa709e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,24 @@ + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/index.jsp new file mode 100755 index 000000000..2321faffc --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-96/src/main/webapp/index.jsp @@ -0,0 +1,22 @@ + +java versionjava version : ${java.version} +Project${pom.name} +Version${pom.version} +custom filter${warPluginFilterConfigurationProperty} \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/MWAR-96/verify.bsh b/Java-base/maven-war-plugin/src/src/it/MWAR-96/verify.bsh new file mode 100644 index 000000000..18a6258eb --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/MWAR-96/verify.bsh @@ -0,0 +1,74 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + + File webappDirectory = new File( target, "MWAR-96-1.0-SNAPSHOT" ); + if ( !webappDirectory.exists() || !webappDirectory.isDirectory() ) + { + System.err.println( "webappDirectory is missing or not a directory." ); + return false; + } + + File param = new File( webappDirectory, "index.jsp" ); + if ( !param.exists() || param.isDirectory() ) + { + System.err.println( "index.jsp file is missing or a directory." ); + return false; + } + String paramContent = FileUtils.fileRead( param ); + + String javaVersion = System.getProperty( "java.version" ); + int indexOf = paramContent.indexOf( "java version : " + javaVersion + "" ); + if ( indexOf < 0 ) + { + paramContent = paramContent.substring( paramContent.indexOf( "java version : " ) ); + System.err.println( "index.jsp not contains java version : " + javaVersion + " but " + + paramContent.substring( 0, paramContent.indexOf( "" ) + 5 ) ); + return false; + } + + indexOf = paramContent.indexOf("okitworks" ); + if ( indexOf < 0 ) + { + System.err.println( "index.jsp not contains okitworks" ); + return false; + } + +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/archiveClasses/pom.xml b/Java-base/maven-war-plugin/src/src/it/archiveClasses/pom.xml new file mode 100644 index 000000000..a7c516631 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/archiveClasses/pom.xml @@ -0,0 +1,70 @@ + + + + + + 4.0.0 + + org.apache.maven.its.mwar-45 + maven-it-mwar-45 + 1.0 + war + + Maven Integration Test :: MWAR-45 + Test a WAR generation with archiveClasses=true + + + UTF-8 + 2020-06-06T06:50:15Z + + + + + javax.servlet + servlet-api + 2.4 + provided + + + commons-logging + commons-logging + 1.0.3 + + + junit + junit + 3.8.2 + test + + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + true + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/java/org/apache/maven/it0016/Person.java b/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/java/org/apache/maven/it0016/Person.java new file mode 100644 index 000000000..4a287e77c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/java/org/apache/maven/it0016/Person.java @@ -0,0 +1,35 @@ +package org.apache.maven.it0016; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public class Person +{ + private String name; + + public void setName( String name ) + { + this.name = name; + } + + public String getName() + { + return name; + } +} diff --git a/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..c1af95de8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/index.html b/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/index.html new file mode 100644 index 000000000..8e81e877d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/archiveClasses/src/main/webapp/index.html @@ -0,0 +1,23 @@ + + + + Hello World + + diff --git a/Java-base/maven-war-plugin/src/src/it/archiveClasses/verify.bsh b/Java-base/maven-war-plugin/src/src/it/archiveClasses/verify.bsh new file mode 100644 index 000000000..9b3da7509 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/archiveClasses/verify.bsh @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.regex.*; + +try +{ + File explodedDir = new File( basedir, "target/maven-it-mwar-45-1.0" ); + System.out.println( "Checking for existence of exploded directory " + explodedDir ); + if ( !explodedDir.isDirectory() ) + { + System.out.println( "FAILURE!" ); + return false; + } + + String[] expectedPaths = { + "index.html", + "WEB-INF/lib/commons-logging-1.0.3.jar", + "WEB-INF/lib/maven-it-mwar-45-1.0.jar", + }; + for ( String path : expectedPaths ) + { + File file = new File( explodedDir, path ); + System.out.println( "Checking for existence of " + file ); + if ( !file.exists() ) + { + System.out.println( "FAILURE!" ); + return false; + } + } + + String[] unexpectedPaths = { + "WEB-INF/classes/org/apache/maven/it0016/Person.class", + "WEB-INF/lib/servlet-api-2.4.jar", + }; + for ( String path : unexpectedPaths ) + { + File file = new File( explodedDir, path ); + System.out.println( "Checking for absence of " + file ); + if ( file.exists() ) + { + System.out.println( "FAILURE!" ); + return false; + } + } + + File warFile = new File( basedir, "target/maven-it-mwar-45-1.0.war" ); + System.out.println( "Checking for existence of " + warFile ); + if ( !warFile.isFile() ) + { + System.out.println( "FAILURE!" ); + return false; + } + + JarFile war = new JarFile( warFile ); + + String[] includedEntries = { + "index.html", + "WEB-INF/lib/maven-it-mwar-45-1.0.jar", + "WEB-INF/lib/commons-logging-1.0.3.jar", + }; + for ( String included : includedEntries ) + { + System.out.println( "Checking for existence of " + included ); + if ( war.getEntry( included ) == null ) + { + System.out.println( "FAILURE!" ); + return false; + } + } + + war.close(); +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/Java-base/maven-war-plugin/src/src/it/default/pom.xml b/Java-base/maven-war-plugin/src/src/it/default/pom.xml new file mode 100644 index 000000000..10554a86b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/default/pom.xml @@ -0,0 +1,66 @@ + + + + + + 4.0.0 + + org.apache.maven.its.it0016 + maven-it-it0016 + 1.0 + war + + Maven Integration Test :: it0016 + Test a WAR generation + + + UTF-8 + + + + + javax.servlet + servlet-api + 2.4 + provided + + + commons-logging + commons-logging + 1.0.3 + + + junit + junit + 3.8.2 + test + + + + + + + org.apache.maven.plugins + maven-war-plugin + @project.version@ + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/default/src/main/java/org/apache/maven/it0016/Person.java b/Java-base/maven-war-plugin/src/src/it/default/src/main/java/org/apache/maven/it0016/Person.java new file mode 100644 index 000000000..4a287e77c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/default/src/main/java/org/apache/maven/it0016/Person.java @@ -0,0 +1,35 @@ +package org.apache.maven.it0016; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public class Person +{ + private String name; + + public void setName( String name ) + { + this.name = name; + } + + public String getName() + { + return name; + } +} diff --git a/Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..c1af95de8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/index.html b/Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/index.html new file mode 100644 index 000000000..8e81e877d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/default/src/main/webapp/index.html @@ -0,0 +1,23 @@ + + + + Hello World + + diff --git a/Java-base/maven-war-plugin/src/src/it/default/verify.bsh b/Java-base/maven-war-plugin/src/src/it/default/verify.bsh new file mode 100644 index 000000000..af15a8196 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/default/verify.bsh @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.regex.*; + +try +{ + File explodedDir = new File( basedir, "target/maven-it-it0016-1.0" ); + System.out.println( "Checking for existence of exploded directory " + explodedDir ); + if ( !explodedDir.isDirectory() ) + { + System.out.println( "FAILURE!" ); + return false; + } + + String[] expectedPaths = { + "index.html", + "WEB-INF/classes/org/apache/maven/it0016/Person.class", + "WEB-INF/lib/commons-logging-1.0.3.jar", + }; + for ( String path : expectedPaths ) + { + File file = new File( explodedDir, path ); + System.out.println( "Checking for existence of " + file ); + if ( !file.exists() ) + { + System.out.println( "FAILURE!" ); + return false; + } + } + + String[] unexpectedPaths = { + "WEB-INF/lib/servlet-api-2.4.jar", + }; + for ( String path : unexpectedPaths ) + { + File file = new File( explodedDir, path ); + System.out.println( "Checking for absence of " + file ); + if ( file.exists() ) + { + System.out.println( "FAILURE!" ); + return false; + } + } + + File warFile = new File( basedir, "target/maven-it-it0016-1.0.war" ); + System.out.println( "Checking for existence of " + warFile ); + if ( !warFile.isFile() ) + { + System.out.println( "FAILURE!" ); + return false; + } + + JarFile war = new JarFile( warFile ); + + String[] includedEntries = { + "WEB-INF/classes/org/apache/maven/it0016/Person.class", + "WEB-INF/lib/commons-logging-1.0.3.jar", + }; + for ( String included : includedEntries ) + { + System.out.println( "Checking for existence of " + included ); + if ( war.getEntry( included ) == null ) + { + System.out.println( "FAILURE!" ); + return false; + } + } + + war.close(); +} +catch( Throwable t ) +{ + t.printStackTrace(); + return false; +} + +return true; diff --git a/Java-base/maven-war-plugin/src/src/it/manifest-content/pom.xml b/Java-base/maven-war-plugin/src/src/it/manifest-content/pom.xml new file mode 100644 index 000000000..bcfa76d0b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/manifest-content/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + test + manifest-content + war + 1.0-SNAPSHOT + Maven War Manifest Content Test + + war plugin it + + + + + maven-war-plugin + @pom.version@ + + false + + true + + true + true + + + + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/manifest-content/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/manifest-content/src/main/webapp/index.jsp new file mode 100644 index 000000000..41db44eef --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/manifest-content/src/main/webapp/index.jsp @@ -0,0 +1,20 @@ +<%-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --%> + +<% response.sendRedirect( request.getContextPath() + "/groupSummary.action" ); %> diff --git a/Java-base/maven-war-plugin/src/src/it/manifest-content/verify.bsh b/Java-base/maven-war-plugin/src/src/it/manifest-content/verify.bsh new file mode 100644 index 000000000..8980a6f86 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/manifest-content/verify.bsh @@ -0,0 +1,96 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or not a directory." ); + return false; + } + + File artifact = new File( target, "manifest-content-1.0-SNAPSHOT.war" ); + if ( !artifact.exists() || artifact.isDirectory() ) + { + System.err.println( "artifact file is missing or a directory." ); + return false; + } + + JarFile jar = new JarFile( artifact ); + + Attributes manifest = jar.getManifest().getMainAttributes(); + + if ( !"Maven War Manifest Content Test".equals( manifest.get( Attributes.Name.SPECIFICATION_TITLE ) ) ) + { + System.err.println( "Incorrect '" + Attributes.Name.SPECIFICATION_TITLE.toString() + "' manifest entry: " + + manifest.get( Attributes.Name.SPECIFICATION_TITLE ) ); + return false; + } + + if ( !"1.0".equals( manifest.get( Attributes.Name.SPECIFICATION_VERSION ) ) ) + { + System.err.println( "Incorrect '" + Attributes.Name.SPECIFICATION_VERSION.toString() + "' manifest entry: " + + manifest.get( Attributes.Name.SPECIFICATION_VERSION ) ); + return false; + } + + if ( !"war plugin it".equals( manifest.get( Attributes.Name.SPECIFICATION_VENDOR ) ) ) + { + System.err.println( "Incorrect '" + Attributes.Name.SPECIFICATION_VENDOR.toString() + "' manifest entry: " + + manifest.get( Attributes.Name.SPECIFICATION_VENDOR ) ); + return false; + } + + if ( !"Maven War Manifest Content Test".equals( manifest.get( Attributes.Name.IMPLEMENTATION_TITLE ) ) ) + { + System.err.println( "Incorrect '" + Attributes.Name.IMPLEMENTATION_TITLE.toString() + "' manifest entry: " + + manifest.get( Attributes.Name.IMPLEMENTATION_TITLE ) ); + return false; + } + + if ( !"1.0-SNAPSHOT".equals( manifest.get( Attributes.Name.IMPLEMENTATION_VERSION ) ) ) + { + System.err.println( "Incorrect '" + Attributes.Name.IMPLEMENTATION_VERSION.toString() + "' manifest entry: " + + manifest.get( Attributes.Name.IMPLEMENTATION_VERSION ) ); + return false; + } + + if ( !"war plugin it".equals( manifest.get( Attributes.Name.IMPLEMENTATION_VENDOR ) ) ) + { + System.err.println( "Incorrect '" + Attributes.Name.IMPLEMENTATION_VENDOR.toString() + "' manifest entry: " + + manifest.get( Attributes.Name.IMPLEMENTATION_VENDOR ) ); + return false; + } +} +catch( Throwable e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/invoker.properties b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/invoker.properties new file mode 100644 index 000000000..3376b9e49 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean install diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/pom.xml b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/pom.xml new file mode 100644 index 000000000..63c55d499 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + debug.war + overlay-excludes + pom + 1.0-SNAPSHOT + test of overlay with exclusions + http://maven.apache.org + + war-overlay + war-exclude-overlay + + + + + + maven-war-plugin + @pom.version@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/verify.bsh b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/verify.bsh new file mode 100644 index 000000000..0ba9a0b50 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/verify.bsh @@ -0,0 +1,31 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + + + File testFile = new File( basedir, "war-exclude-overlay/target/war-exclude-overlay-1.0-SNAPSHOT/lib/js/something/else.js"); + if ( testFile.exists() ) + { + System.err.println( "exclude didn't exclude." ); + return false; + } + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/pom.xml b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/pom.xml new file mode 100644 index 000000000..c735de79a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/pom.xml @@ -0,0 +1,61 @@ + + + + 4.0.0 + + debug.war + overlay-excludes + 1.0-SNAPSHOT + + war-exclude-overlay + war + + + + + debug.war + war-overlay + 1.0-SNAPSHOT + war + runtime + + + + + + + org.apache.maven.plugins + maven-war-plugin + + + + debug.war + war-overlay + + lib/js/** + + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..fc6e240ce --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-exclude-overlay/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/pom.xml b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/pom.xml new file mode 100644 index 000000000..80f19956a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/pom.xml @@ -0,0 +1,30 @@ + + + + 4.0.0 + + debug.war + overlay-excludes + 1.0-SNAPSHOT + + war-overlay + war + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..357cf72ca --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/lib/js/something/else.js b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/lib/js/something/else.js new file mode 100644 index 000000000..b7bb67336 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-excludes/war-overlay/src/main/webapp/lib/js/something/else.js @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* a javascript file, sort of */ diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/invoker.properties b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/invoker.properties new file mode 100644 index 000000000..3376b9e49 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean install diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/pom.xml b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/pom.xml new file mode 100644 index 000000000..ade7a13a3 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + debug.war + overlay-keeps-contextxml + pom + 1.0-SNAPSHOT + test that default overlay keeps META-INF/context.xml + http://maven.apache.org + + war1-with-contextxml + war2-result + + + + + + maven-war-plugin + @pom.version@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/verify.bsh b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/verify.bsh new file mode 100644 index 000000000..f0c99d7ba --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/verify.bsh @@ -0,0 +1,31 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; +import org.codehaus.plexus.util.*; + + + File testFile = new File( basedir, "war2-result/target/war2-result-1.0-SNAPSHOT/META-INF/context.xml"); + if ( !testFile.exists() ) + { + System.err.println( "war1 META-INF/context.xml lost in overlay process" ); + return false; + } + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/pom.xml b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/pom.xml new file mode 100644 index 000000000..cfa52bf19 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/pom.xml @@ -0,0 +1,31 @@ + + + + 4.0.0 + + debug.war + overlay-keeps-contextxml + 1.0-SNAPSHOT + + war1-with-contextxml + war + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/META-INF/context.xml b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/META-INF/context.xml new file mode 100644 index 000000000..b14753a76 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/META-INF/context.xml @@ -0,0 +1,19 @@ + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..c873e8a02 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war1-with-contextxml/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/pom.xml b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/pom.xml new file mode 100644 index 000000000..3656cf311 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + debug.war + overlay-keeps-contextxml + 1.0-SNAPSHOT + + war2-result + war + + + + debug.war + war1-with-contextxml + 1.0-SNAPSHOT + war + runtime + + + diff --git a/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..c873e8a02 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/overlay-keeps-contextxml/war2-result/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/invoker.properties b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/invoker.properties new file mode 100644 index 000000000..c743aa4f3 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean package diff --git a/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/pom.xml b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/pom.xml new file mode 100644 index 000000000..8c882baac --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + test + scope-depdency-same-artifact + war + 1.0-SNAPSHOT + Maven Simple War Project Test + + + + maven-war-plugin + @pom.version@ + + + + + + + + + org.codehaus.plexus + plexus-utils + 1.4.7 + + + org.codehaus.plexus + plexus-utils + sources + 1.4.7 + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..873dd4794 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,29 @@ + + + + + + + Maven Simple War Project Test + + + diff --git a/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/index.jsp new file mode 100644 index 000000000..41db44eef --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/src/main/webapp/index.jsp @@ -0,0 +1,20 @@ +<%-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --%> + +<% response.sendRedirect( request.getContextPath() + "/groupSummary.action" ); %> diff --git a/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/verify.bsh b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/verify.bsh new file mode 100644 index 000000000..057d78828 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/scoped-dependency-same-artifact/verify.bsh @@ -0,0 +1,88 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or a directory." ); + return false; + } + + File webappDirectory = new File( target, "scope-depdency-same-artifact-1.0-SNAPSHOT" ); + if ( !webappDirectory.exists() || !webappDirectory.isDirectory() ) + { + System.err.println( "webappDirectory is missing or not a directory." ); + return false; + } + + File webInfFile = new File( webappDirectory, "WEB-INF/web.xml" ); + if ( !webInfFile.exists() || webInfFile.isDirectory() ) + { + System.err.println( "webInfFile is missing or a directory." ); + return false; + } + + File indexJsp = new File( webappDirectory, "index.jsp" ); + if ( !indexJsp.exists() || indexJsp.isDirectory() ) + { + System.err.println( "indexJsp is missing or a directory." ); + return false; + } + + File warFile = new File( target, "scope-depdency-same-artifact-1.0-SNAPSHOT.war" ); + if ( !warFile.exists() || warFile.isDirectory() ) + { + System.err.println( "warFile is missing or a directory." ); + return false; + } + + File libDir = new File( webappDirectory, "WEB-INF/lib" ); + if ( !libDir.exists() || !libDir.isDirectory() ) + { + System.err.println( "WEB-INF/lib is missing or not a directory." ); + return false; + } + + File plexusUtilsDependency = new File( libDir, "plexus-utils-1.4.7.jar" ); + if ( !plexusUtilsDependency.exists() || plexusUtilsDependency.isDirectory() ) + { + System.err.println( "plexus-utils-1.4.7.jar is missing or a directory." ); + return false; + } + File plexusUtilsSourceDependency = new File( libDir, "plexus-utils-1.4.7-sources.jar" ); + if ( !plexusUtilsSourceDependency.exists() || plexusUtilsSourceDependency.isDirectory() ) + { + System.err.println( "plexus-utils-1.4.7-sources.jar is missing or a directory." ); + return false; + } +} +catch( IOException e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/settings.xml b/Java-base/maven-war-plugin/src/src/it/settings.xml new file mode 100644 index 000000000..c8f77f0b7 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/settings.xml @@ -0,0 +1,55 @@ + + + + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/invoker.properties b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/invoker.properties new file mode 100644 index 000000000..692791cf1 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/invoker.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean package +invoker.debug = true diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/pom.xml b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/pom.xml new file mode 100644 index 000000000..31cc0568f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + test + simple-war-no-webxml + war + 1.0-SNAPSHOT + Maven Simple War Project Test + + + + maven-war-plugin + @pom.version@ + + false + + + + + + + org.codehaus.plexus + plexus-utils + 1.4.6 + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/src/main/webapp/index.jsp new file mode 100644 index 000000000..41db44eef --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/src/main/webapp/index.jsp @@ -0,0 +1,20 @@ +<%-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --%> + +<% response.sendRedirect( request.getContextPath() + "/groupSummary.action" ); %> diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/verify.bsh b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/verify.bsh new file mode 100644 index 000000000..b163bbe86 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-no-webxml/verify.bsh @@ -0,0 +1,82 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or a directory." ); + return false; + } + + File webappDirectory = new File( target, "simple-war-no-webxml-1.0-SNAPSHOT" ); + if ( !webappDirectory.exists() || !webappDirectory.isDirectory() ) + { + System.err.println( "webappDirectory is missing or not a directory." ); + return false; + } + + File webInfFile = new File( webappDirectory, "WEB-INF/web.xml" ); + if ( webInfFile.exists()) + { + System.err.println( "web.xml should not be there." ); + return false; + } + + File indexJsp = new File( webappDirectory, "index.jsp" ); + if ( !indexJsp.exists() || indexJsp.isDirectory() ) + { + System.err.println( "indexJsp is missing or a directory." ); + return false; + } + + File warFile = new File( target, "simple-war-no-webxml-1.0-SNAPSHOT.war" ); + if ( !warFile.exists() || warFile.isDirectory() ) + { + System.err.println( "warFile is missing or a directory." ); + return false; + } + + File libDir = new File( webappDirectory, "WEB-INF/lib" ); + if ( !libDir.exists() || !libDir.isDirectory() ) + { + System.err.println( "WEB-INF/lib is missing or not a directory." ); + return false; + } + + File plexusUtilsDependency = new File( libDir, "plexus-utils-1.4.6.jar" ); + if ( !plexusUtilsDependency.exists() || plexusUtilsDependency.isDirectory() ) + { + System.err.println( "plexus-utils-1.4.6.jar is missing or a directory." ); + return false; + } +} +catch( IOException e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-project/invoker.properties b/Java-base/maven-war-plugin/src/src/it/simple-war-project/invoker.properties new file mode 100644 index 000000000..c743aa4f3 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-project/invoker.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +invoker.goals=clean package diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-project/pom.xml b/Java-base/maven-war-plugin/src/src/it/simple-war-project/pom.xml new file mode 100644 index 000000000..a70c4e8a6 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-project/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + test + simple-war-project + war + 1.0-SNAPSHOT + Maven Simple War Project Test + maven test it + + + 2020-05-01T12:12:12Z + + + + + + maven-war-plugin + @pom.version@ + + + true + + + + + + + + org.codehaus.plexus + plexus-utils + 1.4.6 + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..488bc9739 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,29 @@ + + + + + + + ${pom.description} + + + diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/index.jsp new file mode 100755 index 000000000..4e817f7e9 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-project/src/main/webapp/index.jsp @@ -0,0 +1,20 @@ +<%-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. + --%> + +<% response.sendRedirect( request.getContextPath() + "/groupSummary.action" ); %> diff --git a/Java-base/maven-war-plugin/src/src/it/simple-war-project/verify.bsh b/Java-base/maven-war-plugin/src/src/it/simple-war-project/verify.bsh new file mode 100644 index 000000000..7dc3d728a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/simple-war-project/verify.bsh @@ -0,0 +1,96 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; + +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + File target = new File( basedir, "target" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "target file is missing or a directory." ); + return false; + } + + File webappDirectory = new File( target, "simple-war-project-1.0-SNAPSHOT" ); + if ( !webappDirectory.exists() || !webappDirectory.isDirectory() ) + { + System.err.println( "webappDirectory is missing or not a directory." ); + return false; + } + + File webInfFile = new File( webappDirectory, "WEB-INF/web.xml" ); + if ( !webInfFile.exists() || webInfFile.isDirectory() ) + { + System.err.println( "webInfFile is missing or a directory." ); + return false; + } + + + + String paramContent = FileUtils.fileRead( webInfFile, "UTF-8" ); + + + int indexOf = paramContent.indexOf( "maven test it" ); + if ( indexOf < 0 ) + { + System.err.println( "web.xml not contains maven test it" ); + return false; + } + + File indexJsp = new File( webappDirectory, "index.jsp" ); + if ( !indexJsp.exists() || indexJsp.isDirectory() ) + { + System.err.println( "indexJsp is missing or a directory." ); + return false; + } + + File warFile = new File( target, "simple-war-project-1.0-SNAPSHOT.war" ); + if ( !warFile.exists() || warFile.isDirectory() ) + { + System.err.println( "warFile is missing or a directory." ); + return false; + } + + File libDir = new File( webappDirectory, "WEB-INF/lib" ); + if ( !libDir.exists() || !libDir.isDirectory() ) + { + System.err.println( "WEB-INF/lib is missing or not a directory." ); + return false; + } + + File plexusUtilsDependency = new File( libDir, "plexus-utils-1.4.6.jar" ); + if ( !plexusUtilsDependency.exists() || plexusUtilsDependency.isDirectory() ) + { + System.err.println( "plexus-utils-1.4.6.jar is missing or a directory." ); + return false; + } +} +catch( IOException e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/parent/pom.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/parent/pom.xml new file mode 100644 index 000000000..cee6b8395 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/parent/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + Company Parent + Parent that handles dependencyManagement + + com.edb.finance.example + example-parent + 1.0.0-SNAPSHOT + pom + 2008 + + + + 1 + Valeriy Molyakov + valeriy.molyakov@infopulse.com.ua + EDB Business Partner + http://www.edb.com + + developer + + +2 + + + + + + sample.website + ${website.url}/${project.name} + + + + + + jdbc:oracle:thin:@localhost:1521:orcl + scp://www.yourcompany.com/www/docs/project + + + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/pom.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/pom.xml new file mode 100644 index 000000000..84391162f --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + + com.edb.finance.example + example-parent + 1.0.0-SNAPSHOT + parent/pom.xml + + + + example + pom + + Example Maven Multi-module project For Filtering with Delimiters + http://maven.apache.org + + + parent + web + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/verify.bsh b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/verify.bsh new file mode 100644 index 000000000..dd86ecd4b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/verify.bsh @@ -0,0 +1,108 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; + +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + // Load and check jetty-env.xml + + File target = new File( basedir, "web/target/example-web/WEB-INF" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "web/target/example-web/WEB-INF is missing or is not a directory." ); + return false; + } + + File jettyEnv = new File( target, "jetty-env.xml" ); + if ( !jettyEnv.exists() || jettyEnv.isDirectory() ) + { + System.err.println( "jetty-env.xml is missing or is a directory." ); + return false; + } + + + FileInputStream fis = new FileInputStream ( jettyEnv ); + String paramContent = IOUtil.toString ( fis, "UTF-8" ); + + System.out.println( "content='" + paramContent + "'" ); + + + int indexOf = paramContent.indexOf( "Characters that should be encoded in UTF-8: åäö" ); + if ( indexOf < 0 ) + { + System.err.println( "Non-ascii characters changed encoding during filtering" ); + return false; + } + + indexOf = paramContent.indexOf( "jdbc:oracle:thin:@localhost:1521:orcl" ); + if ( indexOf < 0 ) + { + System.err.println( "jdbc.url not filtered correctly" ); + return false; + } + + indexOf = paramContent.indexOf( "@@jdbc.password@@" ); + if ( indexOf < 0 ) + { + System.err.println( "jdbc.password has been filtered" ); + return false; + } + + // Load and check my.properties + + target = new File( basedir, "web/target/example-web/WEB-INF/classes" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "web/target/example-web/WEB-INF/classes is missing or is not a directory." ); + return false; + } + + File myProperties = new File( target, "my.properties" ); + if ( !myProperties.exists() || myProperties.isDirectory() ) + { + System.err.println( "my.properties is missing or is a directory." ); + return false; + } + + Properties properties = new Properties(); + FileInputStream fis = new FileInputStream( myProperties ); + properties.load( fis ); + fis.close(); + + String property = properties.get( "my.property" ); + System.out.println( "my.property='" + property + "'" ); + if ( !"Characters that should be encoded in ISO-8859-1: åäö".equals( property ) ) + { + System.err.println( "Non-ascii characters has wrong encoding after filtering" ); + return false; + } +} +catch( IOException e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/pom.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/pom.xml new file mode 100644 index 000000000..531d27909 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + + + com.edb.finance.example + example-parent + 1.0.0-SNAPSHOT + ../parent/pom.xml + + + + example-web + war + + example-web Maven Webapp + http://maven.apache.org + + + + junit + junit + 3.8.1 + test + + + + + example-web + + + org.apache.maven.plugins + maven-war-plugin + @pom.version@ + + \ + true + + + ${basedir}/src/main/webresources + true + + + ISO-8859-1 + + @@*@@ + + false + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..36c5645fb --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,26 @@ + + + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/index.jsp new file mode 100755 index 000000000..1f294feb8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +

Hello World!

+ + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/classes/my.properties b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/classes/my.properties new file mode 100644 index 000000000..d07696737 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/classes/my.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +my.property=Characters that should be encoded in ISO-8859-1: åäö \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/jetty-env.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/jetty-env.xml new file mode 100644 index 000000000..68ffb6646 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering-delimiter/web/src/main/webresources/WEB-INF/jetty-env.xml @@ -0,0 +1,40 @@ + + + + + + + jdbc/EventdialogDS + + + @@jdbc.url@@ + @@jdbc.user@@ + \@@jdbc.password@@ + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/parent/pom.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/parent/pom.xml new file mode 100644 index 000000000..cee6b8395 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/parent/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + Company Parent + Parent that handles dependencyManagement + + com.edb.finance.example + example-parent + 1.0.0-SNAPSHOT + pom + 2008 + + + + 1 + Valeriy Molyakov + valeriy.molyakov@infopulse.com.ua + EDB Business Partner + http://www.edb.com + + developer + + +2 + + + + + + sample.website + ${website.url}/${project.name} + + + + + + jdbc:oracle:thin:@localhost:1521:orcl + scp://www.yourcompany.com/www/docs/project + + + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/pom.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/pom.xml new file mode 100644 index 000000000..8412bae76 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + + com.edb.finance.example + example-parent + 1.0.0-SNAPSHOT + parent/pom.xml + + + + example + pom + + Example Maven Multi-module project + http://maven.apache.org + + + parent + web + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/verify.bsh b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/verify.bsh new file mode 100644 index 000000000..6ea7d17be --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/verify.bsh @@ -0,0 +1,115 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.*; + +import org.codehaus.plexus.util.*; + +boolean result = true; + +try +{ + // Load and check jetty-env.xml + + File target = new File( basedir, "web/target/example-web/WEB-INF" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "web/target/example-web/WEB-INF is missing or is not a directory." ); + return false; + } + + File jettyEnv = new File( target, "jetty-env.xml" ); + if ( !jettyEnv.exists() || jettyEnv.isDirectory() ) + { + System.err.println( "jetty-env.xml is missing or is a directory." ); + return false; + } + + + FileInputStream fis = new FileInputStream ( jettyEnv ); + String paramContent = IOUtil.toString ( fis, "UTF-8" ); + + System.out.println( "content='" + paramContent + "'" ); + + + int indexOf = paramContent.indexOf( "Characters that should be encoded in UTF-8: åäö" ); + if ( indexOf < 0 ) + { + System.err.println( "Non-ascii characters changed encoding during filtering" ); + return false; + } + + indexOf = paramContent.indexOf( "Author id: 1" ); + if ( indexOf < 0 ) + { + System.err.println( "project.developers[0].id not filtered correctly" ); + return false; + } + + indexOf = paramContent.indexOf( "jdbc:oracle:thin:@localhost:1521:orcl" ); + if ( indexOf < 0 ) + { + System.err.println( "jdbc.url not filtered correctly" ); + return false; + } + + indexOf = paramContent.indexOf( "${jdbc.password}" ); + if ( indexOf < 0 ) + { + System.err.println( "jdbc.password has been filtered" ); + return false; + } + + // Load and check my.properties + + target = new File( basedir, "web/target/example-web/WEB-INF/classes" ); + if ( !target.exists() || !target.isDirectory() ) + { + System.err.println( "web/target/example-web/WEB-INF/classes is missing or is not a directory." ); + return false; + } + + File myProperties = new File( target, "my.properties" ); + if ( !myProperties.exists() || myProperties.isDirectory() ) + { + System.err.println( "my.properties is missing or is a directory." ); + return false; + } + + Properties properties = new Properties(); + FileInputStream fis = new FileInputStream( myProperties ); + properties.load( fis ); + fis.close(); + + String property = properties.get( "my.property" ); + System.out.println( "my.property='" + property + "'" ); + if ( !"Characters that should be encoded in ISO-8859-1: åäö".equals( property ) ) + { + System.err.println( "Non-ascii characters has wrong encoding after filtering" ); + return false; + } +} +catch( IOException e ) +{ + e.printStackTrace(); + result = false; +} + +return result; diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/pom.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/pom.xml new file mode 100644 index 000000000..071888abc --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/pom.xml @@ -0,0 +1,68 @@ + + + + 4.0.0 + + + com.edb.finance.example + example-parent + 1.0.0-SNAPSHOT + ../parent/pom.xml + + + + example-web + war + + example-web Maven Webapp + http://maven.apache.org + + + + junit + junit + 3.8.1 + test + + + + + example-web + + + org.apache.maven.plugins + maven-war-plugin + @pom.version@ + + \ + true + + + ${basedir}/src/main/webresources + true + + + ISO-8859-1 + + + + + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..36c5645fb --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,26 @@ + + + + + + Archetype Created Web Application + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/index.jsp b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/index.jsp new file mode 100755 index 000000000..1f294feb8 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webapp/index.jsp @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +

Hello World!

+ + diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/classes/my.properties b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/classes/my.properties new file mode 100644 index 000000000..d07696737 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/classes/my.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +my.property=Characters that should be encoded in ISO-8859-1: åäö \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/jetty-env.xml b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/jetty-env.xml new file mode 100644 index 000000000..20d9ebd86 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/it/web-resources-filtering/web/src/main/webresources/WEB-INF/jetty-env.xml @@ -0,0 +1,44 @@ + + + + + + + + jdbc/EventdialogDS + + + ${jdbc.url} + ${jdbc.user} + \${jdbc.password} + + + + diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java new file mode 100644 index 000000000..5595124ca --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/AbstractWarMojo.java @@ -0,0 +1,1152 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; + +import org.apache.maven.archiver.MavenArchiveConfiguration; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.war.overlay.OverlayManager; +import org.apache.maven.plugins.war.packaging.CopyUserManifestTask; +import org.apache.maven.plugins.war.packaging.OverlayPackagingTask; +import org.apache.maven.plugins.war.packaging.WarPackagingContext; +import org.apache.maven.plugins.war.packaging.WarPackagingTask; +import org.apache.maven.plugins.war.packaging.WarProjectPackagingTask; +import org.apache.maven.plugins.war.util.WebappStructure; +import org.apache.maven.project.MavenProject; +import org.apache.maven.shared.filtering.MavenFileFilter; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.apache.maven.shared.filtering.MavenResourcesExecution; +import org.apache.maven.shared.filtering.MavenResourcesFiltering; +import org.apache.maven.shared.utils.StringUtils; +import org.apache.maven.shared.utils.io.FileUtils; +import org.codehaus.plexus.archiver.Archiver; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.archiver.manager.ArchiverManager; + +/** + * Contains common jobs for WAR mojos. + */ +public abstract class AbstractWarMojo + extends AbstractMojo +{ + private static final String META_INF = "META-INF"; + + private static final String WEB_INF = "WEB-INF"; + /** + * Whether or not to fail the build if the web.xml file is missing. Set to false if you + * want your WAR built without a web.xml file. This may be useful if you are building an overlay that + * has no web.xml file. + *

+ * Starting with 3.1.0, this property defaults to false if the project depends on the Servlet + * 3.0 API or newer. + * + * @since 2.1-alpha-2 + */ + @Parameter + protected Boolean failOnMissingWebXml; + + /** + * The Maven project. + */ + @Parameter( defaultValue = "${project}", readonly = true, required = true ) + private MavenProject project; + + /** + * The directory containing compiled classes. + */ + @Parameter( defaultValue = "${project.build.outputDirectory}", required = true, readonly = true ) + private File classesDirectory; + + /** + * Whether a JAR file will be created for the classes in the webapp. Using this optional configuration parameter + * will make the compiled classes to be archived into a JAR file in /WEB-INF/lib/ and the classes + * directory will then be excluded from the webapp /WEB-INF/classes/. + * + * @since 2.0.1 + */ + @Parameter( defaultValue = "false" ) + private boolean archiveClasses; + + /** + * The encoding to use when copying filtered web resources. + * + * @since 2.3 + */ + @Parameter( defaultValue = "${project.build.sourceEncoding}" ) + private String resourceEncoding; + + /** + * The JAR archiver needed for archiving the classes directory into a JAR file under WEB-INF/lib. + */ + @Component( role = Archiver.class, hint = "jar" ) + private JarArchiver jarArchiver; + + /** + * The directory where the webapp is built. + */ + @Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}", required = true ) + private File webappDirectory; + + /** + * Single directory for extra files to include in the WAR. This is where you place your JSP files. + */ + @Parameter( defaultValue = "${basedir}/src/main/webapp", required = true ) + private File warSourceDirectory; + + /** + * The list of webResources we want to transfer. + */ + @Parameter + private Resource[] webResources; + + /** + * Filters (property files) to include during the interpolation of the pom.xml. + */ + @Parameter + private List filters; + + /** + *

+ * Set of delimiters for expressions to filter within the resources. These delimiters are specified in the form + * 'beginToken*endToken'. If no '*' is given, the delimiter is assumed to be the same for start and end. + *

+ *

+ * So, the default filtering delimiters might be specified as: + *

+ * + *
+     * <delimiters>
+     *   <delimiter>${*}</delimiter>
+     *   <delimiter>@</delimiter>
+     * </delimiters>
+     * 
+ *

+ * Since the '@' delimiter is the same on both ends, we don't need to specify '@*@' (though we can). + *

+ * + * @since 3.0.0 + */ + @Parameter + private LinkedHashSet delimiters; + + /** + * Use default delimiters in addition to custom delimiters, if any. + * + * @since 3.0.0 + */ + @Parameter( defaultValue = "true" ) + private boolean useDefaultDelimiters; + + /** + * The path to the web.xml file to use. + */ + @Parameter + private File webXml; + + /** + * The path to a configuration file for the servlet container. Note that the file name may be different for + * different servlet containers. Apache Tomcat uses a configuration file named context.xml. The file will be copied + * to the META-INF directory. + */ + @Parameter + private File containerConfigXML; + + /** + * Directory to unpack dependent WARs into if needed. + */ + @Parameter( defaultValue = "${project.build.directory}/war/work", required = true ) + private File workDirectory; + + /** + * The file name mapping to use when copying libraries and TLDs. If no file mapping is set (default) the files are + * copied with their standard names. + * + * @since 2.1-alpha-1 + */ + @Parameter + private String outputFileNameMapping; + + /** + */ + @Component( role = ArtifactFactory.class ) + private ArtifactFactory artifactFactory; + + /** + * To look up Archiver/UnArchiver implementations. + */ + @Component( role = ArchiverManager.class ) + private ArchiverManager archiverManager; + + /** + */ + @Component( role = MavenFileFilter.class, hint = "default" ) + private MavenFileFilter mavenFileFilter; + + /** + */ + @Component( role = MavenResourcesFiltering.class, hint = "default" ) + private MavenResourcesFiltering mavenResourcesFiltering; + + /** + * The comma separated list of tokens to include when copying the content of the warSourceDirectory. + */ + @Parameter( defaultValue = "**" ) + private String warSourceIncludes; + + /** + * The comma separated list of tokens to exclude when copying the content of the warSourceDirectory. + */ + @Parameter + private String warSourceExcludes; + + /** + * The comma separated list of tokens to include when doing a WAR overlay. Default is + * {@link org.apache.maven.plugins.war.Overlay#DEFAULT_INCLUDES} + * + */ + @Parameter + private String dependentWarIncludes = StringUtils.join( Overlay.DEFAULT_INCLUDES, "," ); + + /** + * The comma separated list of tokens to exclude when doing a WAR overlay. Default is + * {@link org.apache.maven.plugins.war.Overlay#DEFAULT_EXCLUDES} + * + */ + @Parameter + private String dependentWarExcludes = StringUtils.join( Overlay.DEFAULT_EXCLUDES, "," ); + + /** + * The overlays to apply. Each <overlay> element may contain: + *
    + *
  • id (defaults to currentBuild)
  • + *
  • groupId (if this and artifactId are null, then the current project is treated as its own overlay)
  • + *
  • artifactId (see above)
  • + *
  • classifier
  • + *
  • type
  • + *
  • includes (a list of string patterns)
  • + *
  • excludes (a list of string patterns)
  • + *
  • filtered (defaults to false)
  • + *
  • skip (defaults to false)
  • + *
  • targetPath (defaults to root of webapp structure)
  • + *
+ * + * @since 2.1-alpha-1 + */ + @Parameter + private List overlays = new ArrayList<>(); + + /** + * A list of file extensions that should not be filtered. Will be used when filtering webResources and + * overlays. + * + * @since 2.1-alpha-2 + */ + @Parameter + private List nonFilteredFileExtensions; + + /** + * @since 2.1-alpha-2 + */ + @Parameter( defaultValue = "${session}", readonly = true, required = true ) + private MavenSession session; + + /** + * To filter deployment descriptors. Disabled by default. + * + * @since 2.1-alpha-2 + */ + @Parameter( defaultValue = "false" ) + private boolean filteringDeploymentDescriptors; + + /** + * To escape interpolated values with Windows path c:\foo\bar will be replaced with + * c:\\foo\\bar. + * + * @since 2.1-alpha-2 + */ + @Parameter( defaultValue = "false" ) + private boolean escapedBackslashesInFilePath; + + /** + * Expression preceded with this String won't be interpolated. \${foo} will be replaced with + * ${foo}. + * + * @since 2.1-beta-1 + */ + @Parameter + protected String escapeString; + + /** + * Indicates if zip archives (jar,zip etc) being added to the war should be compressed again. Compressing again can + * result in smaller archive size, but gives noticeably longer execution time. + * + * @since 2.3 + */ + @Parameter( defaultValue = "true" ) + private boolean recompressZippedFiles; + + /** + * @since 2.4 + */ + @Parameter( defaultValue = "false" ) + private boolean includeEmptyDirectories; + + /** + * Stop searching endToken at the end of line + * + * @since 2.4 + */ + @Parameter( defaultValue = "false" ) + private boolean supportMultiLineFiltering; + + /** + * use jvmChmod rather that cli chmod and forking process + * + * @since 2.4 + */ + @Parameter( defaultValue = "true" ) + private boolean useJvmChmod; + + /** + * The archive configuration to use. See
Maven + * Archiver Reference. + */ + @Parameter + private MavenArchiveConfiguration archive = new MavenArchiveConfiguration(); + + /** + * Timestamp for reproducible output archive entries, either formatted as ISO 8601 + * yyyy-MM-dd'T'HH:mm:ssXXX or as an int representing seconds since the epoch (like + * SOURCE_DATE_EPOCH). + * + * @since 3.3.0 + */ + @Parameter( defaultValue = "${project.build.outputTimestamp}" ) + protected String outputTimestamp; + + private final Overlay currentProjectOverlay = Overlay.createInstance(); + + /** + * @return The current overlay. + */ + public Overlay getCurrentProjectOverlay() + { + return currentProjectOverlay; + } + + /** + * Returns a string array of the excludes to be used when copying the content of the WAR source directory. + * + * @return an array of tokens to exclude + */ + protected String[] getExcludes() + { + List excludeList = new ArrayList<>(); + if ( StringUtils.isNotEmpty( warSourceExcludes ) ) + { + excludeList.addAll( Arrays.asList( StringUtils.split( warSourceExcludes, "," ) ) ); + } + + // if webXML is specified, omit the one in the source directory + if ( webXml != null && StringUtils.isNotEmpty( webXml.getName() ) ) + { + excludeList.add( "**/" + WEB_INF + "/web.xml" ); + } + + // if contextXML is specified, omit the one in the source directory + if ( containerConfigXML != null && StringUtils.isNotEmpty( containerConfigXML.getName() ) ) + { + excludeList.add( "**/" + META_INF + "/" + containerConfigXML.getName() ); + } + + return excludeList.toArray( new String[excludeList.size()] ); + } + + /** + * Returns a string array of the includes to be used when assembling/copying the WAR. + * + * @return an array of tokens to include + */ + protected String[] getIncludes() + { + return StringUtils.split( StringUtils.defaultString( warSourceIncludes ), "," ); + } + + /** + * Returns a string array of the excludes to be used when adding dependent WAR as an overlay onto this WAR. + * + * @return an array of tokens to exclude + */ + protected String[] getDependentWarExcludes() + { + return StringUtils.split( StringUtils.defaultString( dependentWarExcludes ), "," ); + } + + /** + * Returns a string array of the includes to be used when adding dependent WARs as an overlay onto this WAR. + * + * @return an array of tokens to include + */ + protected String[] getDependentWarIncludes() + { + return StringUtils.split( StringUtils.defaultString( dependentWarIncludes ), "," ); + } + + /** + * @param webapplicationDirectory The web application directory. + * @throws MojoExecutionException In case of failure. + * @throws MojoFailureException In case of failure. + */ + public void buildExplodedWebapp( File webapplicationDirectory ) + throws MojoExecutionException, MojoFailureException + { + webapplicationDirectory.mkdirs(); + + try + { + buildWebapp( project, webapplicationDirectory ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not build webapp", e ); + } + } + + /** + * Builds the webapp for the specified project with the new packaging task thingy. + * Classes, libraries and tld files are copied to the webappDirectory during this phase. + * + * @param mavenProject the maven project + * @param webapplicationDirectory the target directory + * @throws MojoExecutionException if an error occurred while packaging the webapp + * @throws MojoFailureException if an unexpected error occurred while packaging the webapp + * @throws IOException if an error occurred while copying the files + */ + public void buildWebapp( MavenProject mavenProject, File webapplicationDirectory ) + throws MojoExecutionException, MojoFailureException, IOException + { + + WebappStructure structure = new WebappStructure( mavenProject.getDependencies() ); + + // CHECKSTYLE_OFF: LineLength + final long startTime = System.currentTimeMillis(); + getLog().info( "Assembling webapp [" + mavenProject.getArtifactId() + "] in [" + webapplicationDirectory + "]" ); + + final OverlayManager overlayManager = + new OverlayManager( overlays, mavenProject, getDependentWarIncludes(), getDependentWarExcludes(), + currentProjectOverlay ); + // CHECKSTYLE_ON: LineLength + List defaultFilterWrappers; + try + { + MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution(); + mavenResourcesExecution.setEscapeString( escapeString ); + mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering ); + mavenResourcesExecution.setMavenProject( mavenProject ); + + // if these are NOT set, just use the defaults, which are '${*}' and '@'. + mavenResourcesExecution.setDelimiters( delimiters, useDefaultDelimiters ); + + if ( nonFilteredFileExtensions != null ) + { + mavenResourcesExecution.setNonFilteredFileExtensions( nonFilteredFileExtensions ); + } + + if ( filters == null ) + { + filters = getProject().getBuild().getFilters(); + } + mavenResourcesExecution.setFilters( filters ); + mavenResourcesExecution.setEscapedBackslashesInFilePath( escapedBackslashesInFilePath ); + mavenResourcesExecution.setMavenSession( this.session ); + mavenResourcesExecution.setEscapeString( this.escapeString ); + mavenResourcesExecution.setSupportMultiLineFiltering( supportMultiLineFiltering ); + + defaultFilterWrappers = mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution ); + + } + catch ( MavenFilteringException e ) + { + getLog().error( "fail to build filtering wrappers " + e.getMessage() ); + throw new MojoExecutionException( e.getMessage(), e ); + } + + final WarPackagingContext context = + new DefaultWarPackagingContext( webapplicationDirectory, structure, overlayManager, defaultFilterWrappers, + getNonFilteredFileExtensions(), filteringDeploymentDescriptors, + this.artifactFactory, resourceEncoding, useJvmChmod, failOnMissingWebXml, + outputTimestamp ); + + final List packagingTasks = getPackagingTasks( overlayManager ); + + for ( WarPackagingTask warPackagingTask : packagingTasks ) + { + warPackagingTask.performPackaging( context ); + } + + getLog().debug( "Webapp assembled in [" + ( System.currentTimeMillis() - startTime ) + " msecs]" ); + + } + + /** + * Returns a List of the {@link org.apache.maven.plugins.war.packaging.WarPackagingTask} + * instances to invoke to perform the packaging. + * + * @param overlayManager the overlay manager + * @return the list of packaging tasks + * @throws MojoExecutionException if the packaging tasks could not be built + */ + private List getPackagingTasks( OverlayManager overlayManager ) + throws MojoExecutionException + { + final List packagingTasks = new ArrayList<>(); + + packagingTasks.add( new CopyUserManifestTask() ); + + final List resolvedOverlays = overlayManager.getOverlays(); + for ( Overlay overlay : resolvedOverlays ) + { + if ( overlay.isCurrentProject() ) + { + packagingTasks.add( new WarProjectPackagingTask( webResources, webXml, containerConfigXML, + currentProjectOverlay ) ); + } + else + { + packagingTasks.add( new OverlayPackagingTask( overlay, currentProjectOverlay ) ); + } + } + return packagingTasks; + } + + /** + * WarPackagingContext default implementation + */ + private class DefaultWarPackagingContext + implements WarPackagingContext + { + private final ArtifactFactory artifactFactory; + + private final String resourceEncoding; + + private final WebappStructure webappStructure; + + private final File webappDirectory; + + private final OverlayManager overlayManager; + + private final List filterWrappers; + + private List nonFilteredFileExtensions; + + private boolean filteringDeploymentDescriptors; + + private boolean useJvmChmod; + + private final Boolean failOnMissingWebXml; + + private final Collection outdatedResources; + + private final String outputTimestamp; + + /** + * @param webappDirectory The web application directory. + * @param webappStructure The web app structure. + * @param overlayManager The overlay manager. + * @param filterWrappers The filter wrappers + * @param nonFilteredFileExtensions The non filtered file extensions. + * @param filteringDeploymentDescriptors The filtering deployment descriptors. + * @param artifactFactory The artifact factory. + * @param resourceEncoding The resource encoding. + * @param useJvmChmod use Jvm chmod or not. + * @param failOnMissingWebXml Flag to check whether we should ignore missing web.xml or not + * @param outputTimestamp the output timestamp for reproducible archive creation + */ + DefaultWarPackagingContext( final File webappDirectory, final WebappStructure webappStructure, + final OverlayManager overlayManager, + List filterWrappers, + List nonFilteredFileExtensions, + boolean filteringDeploymentDescriptors, ArtifactFactory artifactFactory, + String resourceEncoding, boolean useJvmChmod, + final Boolean failOnMissingWebXml, String outputTimestamp ) + { + this.webappDirectory = webappDirectory; + this.webappStructure = webappStructure; + this.overlayManager = overlayManager; + this.filterWrappers = filterWrappers; + this.artifactFactory = artifactFactory; + this.filteringDeploymentDescriptors = filteringDeploymentDescriptors; + this.nonFilteredFileExtensions = + nonFilteredFileExtensions == null ? Collections.emptyList() : nonFilteredFileExtensions; + this.resourceEncoding = resourceEncoding; + // This is kinda stupid but if we loop over the current overlays and we request the path structure + // it will register it. This will avoid wrong warning messages in a later phase + for ( String overlayId : overlayManager.getOverlayIds() ) + { + webappStructure.getStructure( overlayId ); + } + this.useJvmChmod = useJvmChmod; + this.failOnMissingWebXml = failOnMissingWebXml; + + if ( !webappDirectory.exists() ) + { + outdatedResources = Collections.emptyList(); + } + else if ( getWarSourceDirectory().toPath().equals( webappDirectory.toPath() ) ) + { + getLog().info( "Can't detect outdated resources when running inplace goal" ); + outdatedResources = Collections.emptyList(); + } + else + { + outdatedResources = new ArrayList<>(); + try + { + Files.walkFileTree( webappDirectory.toPath(), new SimpleFileVisitor() + { + @Override + public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) + throws IOException + { + outdatedResources.add( webappDirectory.toPath().relativize( file ).toString() ); + return super.visitFile( file, attrs ); + } + } ); + } + catch ( IOException e ) + { + getLog().warn( "Can't detect outdated resources", e ); + } + } + this.outputTimestamp = outputTimestamp; + } + + @Override + public MavenProject getProject() + { + return project; + } + + @Override + public File getWebappDirectory() + { + return webappDirectory; + } + + @Override + public File getClassesDirectory() + { + return classesDirectory; + } + + @Override + public Log getLog() + { + return AbstractWarMojo.this.getLog(); + } + + @Override + public String getOutputFileNameMapping() + { + return outputFileNameMapping; + } + + @Override + public File getWebappSourceDirectory() + { + return warSourceDirectory; + } + + @Override + public String[] getWebappSourceIncludes() + { + return getIncludes(); + } + + @Override + public String[] getWebappSourceExcludes() + { + return getExcludes(); + } + + @Override + public boolean isWebappSourceIncludeEmptyDirectories() + { + return includeEmptyDirectories; + } + + @Override + public boolean archiveClasses() + { + return archiveClasses; + } + + @Override + public File getOverlaysWorkDirectory() + { + return workDirectory; + } + + @Override + public ArchiverManager getArchiverManager() + { + return archiverManager; + } + + @Override + public MavenArchiveConfiguration getArchive() + { + return archive; + } + + @Override + public JarArchiver getJarArchiver() + { + return jarArchiver; + } + + @Override + public List getFilters() + { + return filters; + } + + @Override + public WebappStructure getWebappStructure() + { + return webappStructure; + } + + @Override + public List getOwnerIds() + { + return overlayManager.getOverlayIds(); + } + + @Override + public MavenFileFilter getMavenFileFilter() + { + return mavenFileFilter; + } + + @Override + public List getFilterWrappers() + { + return filterWrappers; + } + + @Override + public boolean isNonFilteredExtension( String fileName ) + { + return !mavenResourcesFiltering.filteredFileExtension( fileName, nonFilteredFileExtensions ); + } + + @Override + public boolean isFilteringDeploymentDescriptors() + { + return filteringDeploymentDescriptors; + } + + @Override + public ArtifactFactory getArtifactFactory() + { + return this.artifactFactory; + } + + @Override + public MavenSession getSession() + { + return session; + } + + @Override + public String getResourceEncoding() + { + return resourceEncoding; + } + + @Override + public boolean isUseJvmChmod() + { + return useJvmChmod; + } + + @Override + public Boolean isFailOnMissingWebXml() + { + return failOnMissingWebXml; + } + + @Override + public void addResource( String resource ) + { + outdatedResources.remove( resource.replace( '/', File.separatorChar ) ); + } + + @Override + public void deleteOutdatedResources() + { + for ( String resource : outdatedResources ) + { + getLog().info( "deleting outdated resource " + resource ); + new File( getWebappDirectory(), resource ).delete(); + } + } + + @Override + public String getOutputTimestamp() + { + return outputTimestamp; + } + } + + /** + * @return The Maven Project. + */ + public MavenProject getProject() + { + return project; + } + + /** + * @param project The project to be set. + */ + public void setProject( MavenProject project ) + { + this.project = project; + } + + /** + * @return the classes directory. + */ + public File getClassesDirectory() + { + return classesDirectory; + } + + /** + * @param classesDirectory The classes directory to be set. + */ + public void setClassesDirectory( File classesDirectory ) + { + this.classesDirectory = classesDirectory; + } + + /** + * @return {@link #webappDirectory} + */ + public File getWebappDirectory() + { + return webappDirectory; + } + + /** + * @param webappDirectory The web application directory. + */ + public void setWebappDirectory( File webappDirectory ) + { + this.webappDirectory = webappDirectory; + } + + /** + * @return {@link #warSourceDirectory} + */ + public File getWarSourceDirectory() + { + return warSourceDirectory; + } + + /** + * @param warSourceDirectory {@link #warSourceDirectory} + */ + public void setWarSourceDirectory( File warSourceDirectory ) + { + this.warSourceDirectory = warSourceDirectory; + } + + /** + * @return The {@link #webXml} + */ + public File getWebXml() + { + return webXml; + } + + /** + * @param webXml The {@link #webXml} + */ + public void setWebXml( File webXml ) + { + this.webXml = webXml; + } + + /** + * @return {@link #containerConfigXML} + */ + public File getContainerConfigXML() + { + return containerConfigXML; + } + + /** + * @param containerConfigXML {@link #containerConfigXML} + */ + public void setContainerConfigXML( File containerConfigXML ) + { + this.containerConfigXML = containerConfigXML; + } + + /** + * @return {@link #outputFileNameMapping} + */ + public String getOutputFileNameMapping() + { + return outputFileNameMapping; + } + + /** + * @param outputFileNameMapping {@link #outputFileNameMapping} + */ + public void setOutputFileNameMapping( String outputFileNameMapping ) + { + this.outputFileNameMapping = outputFileNameMapping; + } + + /** + * @return {@link #overlays} + */ + public List getOverlays() + { + return overlays; + } + + /** + * @param overlays {@link #overlays} + */ + public void setOverlays( List overlays ) + { + this.overlays = overlays; + } + + /** + * @param overlay add {@link #overlays}. + */ + public void addOverlay( Overlay overlay ) + { + overlays.add( overlay ); + } + + /** + * @return {@link #archiveClasses} + */ + public boolean isArchiveClasses() + { + return archiveClasses; + } + + /** + * @param archiveClasses {@link #archiveClasses} + */ + public void setArchiveClasses( boolean archiveClasses ) + { + this.archiveClasses = archiveClasses; + } + + /** + * @return {@link JarArchiver} + */ + public JarArchiver getJarArchiver() + { + return jarArchiver; + } + + /** + * @param jarArchiver {@link JarArchiver} + */ + public void setJarArchiver( JarArchiver jarArchiver ) + { + this.jarArchiver = jarArchiver; + } + + /** + * @return {@link #webResources}. + */ + public Resource[] getWebResources() + { + return webResources; + } + + /** + * @param webResources {@link #webResources}. + */ + public void setWebResources( Resource[] webResources ) + { + this.webResources = webResources; + } + + /** + * @return {@link #filters} + */ + public List getFilters() + { + return filters; + } + + /** + * @param filters {@link #filters} + */ + public void setFilters( List filters ) + { + this.filters = filters; + } + + /** + * @return {@link #workDirectory} + */ + public File getWorkDirectory() + { + return workDirectory; + } + + /** + * @param workDirectory {@link #workDirectory} + */ + public void setWorkDirectory( File workDirectory ) + { + this.workDirectory = workDirectory; + } + + /** + * @return {@link #warSourceIncludes} + */ + public String getWarSourceIncludes() + { + return warSourceIncludes; + } + + /** + * @param warSourceIncludes {@link #warSourceIncludes} + */ + public void setWarSourceIncludes( String warSourceIncludes ) + { + this.warSourceIncludes = warSourceIncludes; + } + + /** + * @return {@link #warSourceExcludes} + */ + public String getWarSourceExcludes() + { + return warSourceExcludes; + } + + /** + * @param warSourceExcludes {@link #warSourceExcludes} + */ + public void setWarSourceExcludes( String warSourceExcludes ) + { + this.warSourceExcludes = warSourceExcludes; + } + + /** + * @return {@link #archive} + */ + public MavenArchiveConfiguration getArchive() + { + return archive; + } + + /** + * @return {@link #nonFilteredFileExtensions} + */ + public List getNonFilteredFileExtensions() + { + return nonFilteredFileExtensions; + } + + /** + * @param nonFilteredFileExtensions {@link #nonFilteredFileExtensions} + */ + public void setNonFilteredFileExtensions( List nonFilteredFileExtensions ) + { + this.nonFilteredFileExtensions = nonFilteredFileExtensions; + } + + /** + * @return {@link #artifactFactory} + */ + public ArtifactFactory getArtifactFactory() + { + return this.artifactFactory; + } + + /** + * @param artifactFactory {@link #artifactFactory} + */ + public void setArtifactFactory( ArtifactFactory artifactFactory ) + { + this.artifactFactory = artifactFactory; + } + + /** + * @return {@link #session} + */ + protected MavenSession getSession() + { + return this.session; + } + + /** + * @return {@link #recompressZippedFiles} + */ + protected boolean isRecompressZippedFiles() + { + return recompressZippedFiles; + } + + /** + * @return {@link #includeEmptyDirectories} + */ + protected boolean isIncludeEmptyDirectories() + { + return includeEmptyDirectories; + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/Overlay.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/Overlay.java new file mode 100644 index 000000000..c42f1c793 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/Overlay.java @@ -0,0 +1,384 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *

+ * An overlay is a skeleton WAR added to another WAR project in order to inject a functionality, resources or any other + * shared component.

+ * + *

Note that a particular WAR dependency can be added multiple times as an overlay with different includes/excludes + * filter; this allows building a fine grained overwriting policy.

+ * + *

The current project can also be described as an overlay and can not be specified twice. An overlay with no groupId + * and no artifactId represents the current project.

+ * + * @author Stephane Nicoll + */ +public class Overlay +{ + + /** + * The list of default includes. + */ + public static final String[] DEFAULT_INCLUDES = new String[] { "**/**" }; + + /** + * The list of default excludes. + */ + public static final String[] DEFAULT_EXCLUDES = new String[] { "META-INF/MANIFEST.MF" }; + + private String id; + + private String groupId; + + private String artifactId; + + private String classifier = null; + + private String[] includes = DEFAULT_INCLUDES; + + private String[] excludes = DEFAULT_EXCLUDES; + + private boolean filtered = false; + + private boolean skip = false; + + private Artifact artifact; + + private String targetPath; + + /** default overlay type is war */ + private String type = "war"; + + /** + * Create instance. + */ + public Overlay() + { + super(); + } + + /** + * @param groupId {@link #groupId} + * @param artifactId {@link #artifactId} + */ + public Overlay( String groupId, String artifactId ) + { + this(); + this.groupId = groupId; + this.artifactId = artifactId; + } + + /** + * Specify whether this overlay represents the current project or not. + * + * @return true if the overlay represents the current project, false otherwise + */ + public boolean isCurrentProject() + { + return ( groupId == null && artifactId == null ); + } + + /** + * @return {@link Overlay} instance. + */ + public static Overlay createInstance() + { + Overlay overlay = new Overlay(); + overlay.setId( "currentBuild" ); + return overlay; + } + + // Getters and Setters + + /** + * @return The id. + */ + public String getId() + { + if ( id == null ) + { + final StringBuilder sb = new StringBuilder(); + sb.append( getGroupId() ).append( ":" ).append( getArtifactId() ); + if ( getClassifier() != null ) + { + sb.append( ":" ).append( getClassifier() ); + } + id = sb.toString(); + } + return id; + } + + /** + * @param id The id. + */ + public void setId( String id ) + { + this.id = id; + } + + /** + * @return {@link #groupId} + */ + public String getGroupId() + { + return groupId; + } + + /** + * @param groupId {@link #groupId} + */ + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + /** + * @return {@link #artifactId} + */ + public String getArtifactId() + { + return artifactId; + } + + /** + * @param artifactId {@link #artifactId} + */ + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + + /** + * @return {@link #classifier} + */ + public String getClassifier() + { + return classifier; + } + + /** + * @param classifier {@link #classifier} + */ + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + /** + * @return {@link #includes} + */ + public String[] getIncludes() + { + return includes; + } + + /** + * @param includes {@link #includes} + */ + public void setIncludes( String includes ) + { + this.includes = parse( includes ); + } + + /** + * @param includes {@link #includes} + */ + public void setIncludes( String[] includes ) + { + this.includes = includes; + } + + /** + * @return {@link #excludes} + */ + public String[] getExcludes() + { + return excludes; + } + + /** + * @param excludes {@link #excludes} + */ + public void setExcludes( String excludes ) + { + this.excludes = parse( excludes ); + } + + /** + * @param excludes {@link #excludes} + */ + public void setExcludes( String[] excludes ) + { + this.excludes = excludes; + } + + /** + * @return {@link #filtered} + */ + public boolean isFiltered() + { + return filtered; + } + + /** + * @param filtered {@link #filtered} + */ + public void setFiltered( boolean filtered ) + { + this.filtered = filtered; + } + + /** + * @return {@link #skip} + */ + public boolean shouldSkip() + { + return skip; + } + + /** + * @param skip {@link #skip} + */ + public void setSkip( boolean skip ) + { + this.skip = skip; + } + + /** + * @return {@link #artifact} + */ + public Artifact getArtifact() + { + return artifact; + } + + /** + * @param artifact {@link #artifact} + */ + public void setArtifact( Artifact artifact ) + { + this.artifact = artifact; + } + + /** + * @return {@link #targetPath} + */ + public String getTargetPath() + { + return targetPath; + } + + /** + * @param targetPath {@link #targetPath} + */ + public void setTargetPath( String targetPath ) + { + this.targetPath = targetPath; + } + + /** + * @return {@link #type} + */ + public String getType() + { + return type; + } + + /** + * @param type {@link #type} + */ + public void setType( String type ) + { + this.type = type; + } + + @Override + public String toString() + { + return " id " + getId(); + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + Overlay overlay = (Overlay) o; + + if ( excludes != null ? !Arrays.equals( excludes, overlay.excludes ) : overlay.excludes != null ) + { + return false; + } + if ( getId() != null ? !getId().equals( overlay.getId() ) : overlay.getId() != null ) + { + return false; + } + if ( includes != null ? !Arrays.equals( includes, overlay.includes ) : overlay.includes != null ) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result; + result = ( getId() != null ? getId().hashCode() : 0 ); + result = 31 * result + ( includes != null ? includes.hashCode() : 0 ); + result = 31 * result + ( excludes != null ? excludes.hashCode() : 0 ); + return result; + } + + private String[] parse( String s ) + { + final List result = new ArrayList<>(); + if ( s == null ) + { + return result.toArray( new String[result.size()] ); + } + else + { + String[] tokens = s.split( "," ); + for ( String token : tokens ) + { + result.add( token.trim() ); + } + return result.toArray( new String[result.size()] ); + } + } + +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarExplodedMojo.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarExplodedMojo.java new file mode 100644 index 000000000..1327068ce --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarExplodedMojo.java @@ -0,0 +1,46 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Create an exploded webapp in a specified directory. + * + */ +@Mojo( name = "exploded", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, + requiresDependencyResolution = ResolutionScope.RUNTIME ) +public class WarExplodedMojo + extends AbstractWarMojo +{ + @Override + public void execute() + throws MojoExecutionException, MojoFailureException + { + getLog().info( "Exploding webapp" ); + + buildExplodedWebapp( getWebappDirectory() ); + } + +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarInPlaceMojo.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarInPlaceMojo.java new file mode 100644 index 000000000..9cfe02c64 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarInPlaceMojo.java @@ -0,0 +1,43 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Generate the webapp in the WAR source directory. + * + */ +@Mojo( name = "inplace", requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true ) +public class WarInPlaceMojo + extends AbstractWarMojo +{ + @Override + public void execute() + throws MojoExecutionException, MojoFailureException + { + getLog().info( "Generating webapp in source directory [" + getWarSourceDirectory() + "]" ); + + buildExplodedWebapp( getWarSourceDirectory() ); + } +} \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarMojo.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarMojo.java new file mode 100644 index 000000000..e30c57151 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/WarMojo.java @@ -0,0 +1,574 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.plugins.war.util.ClassesPackager; +import org.apache.maven.project.MavenProjectHelper; +import org.codehaus.plexus.archiver.Archiver; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.jar.ManifestException; +import org.codehaus.plexus.archiver.war.WarArchiver; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.StringUtils; + +/** + * Build a WAR file. + * + * @author Emmanuel Venisse + */ +@Mojo( name = "war", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME ) +public class WarMojo + extends AbstractWarMojo +{ + /** + * The directory for the generated WAR. + */ + @Parameter( defaultValue = "${project.build.directory}", required = true ) + private String outputDirectory; + + /** + * The name of the generated WAR. + */ + @Parameter( defaultValue = "${project.build.finalName}", required = true, readonly = true ) + private String warName; + + /** + * Classifier to add to the generated WAR. If given, the artifact will be an attachment instead. The classifier will + * not be applied to the JAR file of the project - only to the WAR file. + */ + @Parameter + private String classifier; + + /** + * The comma separated list of tokens to exclude from the WAR before packaging. This option may be used to implement + * the skinny WAR use case. Note that you can use the Java Regular Expressions engine to include and exclude + * specific pattern using the expression %regex[]. Hint: read the about (?!Pattern). + * + * @since 2.1-alpha-2 + */ + @Parameter + private String packagingExcludes; + + /** + * The comma separated list of tokens to include in the WAR before packaging. By default everything is included. + * This option may be used to implement the skinny WAR use case. Note that you can use the Java Regular Expressions + * engine to include and exclude specific pattern using the expression %regex[]. + * + * @since 2.1-beta-1 + */ + @Parameter + private String packagingIncludes; + + /** + * The WAR archiver. + */ + @Component( role = Archiver.class, hint = "war" ) + private WarArchiver warArchiver; + + /** + */ + @Component + private MavenProjectHelper projectHelper; + + /** + * Whether this is the main artifact being built. Set to false if you don't want to install or deploy + * it to the local repository instead of the default one in an execution. + */ + @Parameter( defaultValue = "true" ) + private boolean primaryArtifact; + + /** + * Whether classes (that is the content of the WEB-INF/classes directory) should be attached to the project as an + * additional artifact. + *

+ * By default the classifier for the additional artifact is 'classes'. You can change it with the + * someclassifier]]> parameter. + *

+ *

+ * If this parameter true, another project can depend on the classes by writing something like: + * + *

+     * 
+     *   myGroup
+     *   myArtifact
+     *   myVersion
+     *   classes
+     * ]]>
+     * 
+ *

+ * + * @since 2.1-alpha-2 + */ + @Parameter( defaultValue = "false" ) + private boolean attachClasses; + + /** + * The classifier to use for the attached classes artifact. + * + * @since 2.1-alpha-2 + */ + @Parameter( defaultValue = "classes" ) + private String classesClassifier; + + /** + * You can skip the execution of the plugin if you need to. Its use is NOT RECOMMENDED, but quite convenient on + * occasion. + * + * @since 3.0.0 + */ + @Parameter( property = "maven.war.skip", defaultValue = "false" ) + private boolean skip; + + // ---------------------------------------------------------------------- + // Implementation + // ---------------------------------------------------------------------- + + /** + * Executes the WarMojo on the current project. + * + * @throws MojoExecutionException if an error occurred while building the webapp + * @throws MojoFailureException if an error. + */ + @Override + public void execute() + throws MojoExecutionException, MojoFailureException + { + + if ( isSkip() ) + { + getLog().info( "Skipping the execution." ); + return; + } + + File warFile = getTargetWarFile(); + + try + { + performPackaging( warFile ); + } + catch ( DependencyResolutionRequiredException | ArchiverException e ) + { + throw new MojoExecutionException( "Error assembling WAR: " + e.getMessage(), e ); + } + catch ( ManifestException | IOException e ) + { + throw new MojoExecutionException( "Error assembling WAR", e ); + } + } + + /** + * Generates the webapp according to the mode attribute. + * + * @param warFile the target WAR file + * @throws IOException if an error occurred while copying files + * @throws ArchiverException if the archive could not be created + * @throws ManifestException if the manifest could not be created + * @throws DependencyResolutionRequiredException if an error occurred while resolving the dependencies + * @throws MojoExecutionException if the execution failed + * @throws MojoFailureException if a fatal exception occurred + */ + private void performPackaging( File warFile ) + throws IOException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException, + MojoFailureException + { + getLog().info( "Packaging webapp" ); + + buildExplodedWebapp( getWebappDirectory() ); + + MavenArchiver archiver = new MavenArchiver(); + + archiver.setArchiver( warArchiver ); + + archiver.setCreatedBy( "Maven WAR Plugin", "org.apache.maven.plugins", "maven-war-plugin" ); + + archiver.setOutputFile( warFile ); + + // configure for Reproducible Builds based on outputTimestamp value + archiver.configureReproducible( outputTimestamp ); + + getLog().debug( "Excluding " + Arrays.asList( getPackagingExcludes() ) + + " from the generated webapp archive." ); + getLog().debug( "Including " + Arrays.asList( getPackagingIncludes() ) + " in the generated webapp archive." ); + + warArchiver.addDirectory( getWebappDirectory(), getPackagingIncludes(), getPackagingExcludes() ); + + final File webXmlFile = new File( getWebappDirectory(), "WEB-INF/web.xml" ); + if ( webXmlFile.exists() ) + { + warArchiver.setWebxml( webXmlFile ); + } + + warArchiver.setRecompressAddedZips( isRecompressZippedFiles() ); + + warArchiver.setIncludeEmptyDirs( isIncludeEmptyDirectories() ); + + if ( Boolean.FALSE.equals( failOnMissingWebXml ) + || ( failOnMissingWebXml == null && isProjectUsingAtLeastServlet30() ) ) + { + getLog().debug( "Build won't fail if web.xml file is missing." ); + warArchiver.setExpectWebXml( false ); + } + + // create archive + archiver.createArchive( getSession(), getProject(), getArchive() ); + + // create the classes to be attached if necessary + if ( isAttachClasses() ) + { + if ( isArchiveClasses() && getJarArchiver().getDestFile() != null ) + { + // special handling in case of archived classes: MWAR-240 + File targetClassesFile = getTargetClassesFile(); + FileUtils.copyFile( getJarArchiver().getDestFile(), targetClassesFile ); + projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), targetClassesFile ); + } + else + { + ClassesPackager packager = new ClassesPackager(); + final File classesDirectory = packager.getClassesDirectory( getWebappDirectory() ); + if ( classesDirectory.exists() ) + { + getLog().info( "Packaging classes" ); + packager.packageClasses( classesDirectory, getTargetClassesFile(), getJarArchiver(), getSession(), + getProject(), getArchive(), outputTimestamp ); + projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), getTargetClassesFile() ); + } + } + } + + if ( this.classifier != null ) + { + projectHelper.attachArtifact( getProject(), "war", this.classifier, warFile ); + } + else + { + Artifact artifact = getProject().getArtifact(); + if ( primaryArtifact ) + { + artifact.setFile( warFile ); + } + else if ( artifact.getFile() == null || artifact.getFile().isDirectory() ) + { + artifact.setFile( warFile ); + } + } + } + + /** + * Determines if the current Maven project being built uses the Servlet 3.0 API (JSR 315) + * or Jakarta Servlet API. + * If it does then the web.xml file can be omitted. + *

+ * This is done by checking if the interface javax.servlet.annotation.WebServlet + * or jakarta.servlet.annotation.WebServlet is in the compile-time + * dependencies (which includes provided dependencies) of the Maven project. + * + * @return true if the project being built depends on Servlet 3.0 API or Jakarta Servlet API, + * false otherwise. + * @throws DependencyResolutionRequiredException if the compile elements can't be resolved. + * @throws MalformedURLException if the path to a dependency file can't be transformed to a URL. + */ + private boolean isProjectUsingAtLeastServlet30() + throws DependencyResolutionRequiredException, MalformedURLException + { + List classpathElements = getProject().getCompileClasspathElements(); + URL[] urls = new URL[classpathElements.size()]; + for ( int i = 0; i < urls.length; i++ ) + { + urls[i] = new File( classpathElements.get( i ) ).toURI().toURL(); + } + ClassLoader loader = new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() ); + + return hasWebServletAnnotationClassInClasspath( loader ); + } + + private static boolean hasWebServletAnnotationClassInClasspath( ClassLoader loader ) + { + return hasClassInClasspath( loader, "javax.servlet.annotation.WebServlet" ) + || hasClassInClasspath( loader, "jakarta.servlet.annotation.WebServlet" ); + } + + private static boolean hasClassInClasspath( ClassLoader loader, String clazz ) + { + try + { + Class.forName( clazz, false, loader ); + return true; + } + catch ( ClassNotFoundException e ) + { + return false; + } + } + + /** + * @param basedir The basedir + * @param finalName The finalName + * @param classifier The classifier. + * @param type The type. + * @return {@link File} + */ + protected static File getTargetFile( File basedir, String finalName, String classifier, String type ) + { + if ( classifier == null ) + { + classifier = ""; + } + else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) ) + { + classifier = "-" + classifier; + } + + return new File( basedir, finalName + classifier + "." + type ); + } + + /** + * @return The war {@link File} + */ + protected File getTargetWarFile() + { + return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassifier(), "war" ); + + } + + /** + * @return The target class {@link File} + */ + protected File getTargetClassesFile() + { + return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassesClassifier(), "jar" ); + } + + // Getters and Setters + + /** + * @return {@link #classifier} + */ + public String getClassifier() + { + return classifier; + } + + /** + * @param classifier {@link #classifier} + */ + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + /** + * @return The package excludes. + */ + public String[] getPackagingExcludes() + { + if ( StringUtils.isEmpty( packagingExcludes ) ) + { + return new String[0]; + } + else + { + return StringUtils.split( packagingExcludes, "," ); + } + } + + /** + * @param packagingExcludes {@link #packagingExcludes} + */ + public void setPackagingExcludes( String packagingExcludes ) + { + this.packagingExcludes = packagingExcludes; + } + + /** + * @return The packaging includes. + */ + public String[] getPackagingIncludes() + { + if ( StringUtils.isEmpty( packagingIncludes ) ) + { + return new String[] { "**" }; + } + else + { + return StringUtils.split( packagingIncludes, "," ); + } + } + + /** + * @param packagingIncludes {@link #packagingIncludes} + */ + public void setPackagingIncludes( String packagingIncludes ) + { + this.packagingIncludes = packagingIncludes; + } + + /** + * @return {@link #outputDirectory} + */ + public String getOutputDirectory() + { + return outputDirectory; + } + + /** + * @param outputDirectory {@link #outputDirectory} + */ + public void setOutputDirectory( String outputDirectory ) + { + this.outputDirectory = outputDirectory; + } + + /** + * @return {@link #warName} + */ + public String getWarName() + { + return warName; + } + + /** + * @param warName {@link #warName} + */ + public void setWarName( String warName ) + { + this.warName = warName; + } + + /** + * @return {@link #warArchiver} + */ + public WarArchiver getWarArchiver() + { + return warArchiver; + } + + /** + * @param warArchiver {@link #warArchiver} + */ + public void setWarArchiver( WarArchiver warArchiver ) + { + this.warArchiver = warArchiver; + } + + /** + * @return {@link #projectHelper} + */ + public MavenProjectHelper getProjectHelper() + { + return projectHelper; + } + + /** + * @param projectHelper {@link #projectHelper} + */ + public void setProjectHelper( MavenProjectHelper projectHelper ) + { + this.projectHelper = projectHelper; + } + + /** + * @return {@link #primaryArtifact} + */ + public boolean isPrimaryArtifact() + { + return primaryArtifact; + } + + /** + * @param primaryArtifact {@link #primaryArtifact} + */ + public void setPrimaryArtifact( boolean primaryArtifact ) + { + this.primaryArtifact = primaryArtifact; + } + + /** + * @return {@link #attachClasses} + */ + public boolean isAttachClasses() + { + return attachClasses; + } + + /** + * @param attachClasses {@link #attachClasses} + */ + public void setAttachClasses( boolean attachClasses ) + { + this.attachClasses = attachClasses; + } + + /** + * @return {@link #classesClassifier} + */ + public String getClassesClassifier() + { + return classesClassifier; + } + + /** + * @param classesClassifier {@link #classesClassifier} + */ + public void setClassesClassifier( String classesClassifier ) + { + this.classesClassifier = classesClassifier; + } + + /** + * @return {@link #failOnMissingWebXml} + */ + public boolean isFailOnMissingWebXml() + { + return failOnMissingWebXml; + } + + /** + * @param failOnMissingWebXml {@link #failOnMissingWebXml} + */ + public void setFailOnMissingWebXml( boolean failOnMissingWebXml ) + { + this.failOnMissingWebXml = failOnMissingWebXml; + } + + public boolean isSkip() + { + return skip; + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/DefaultOverlay.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/DefaultOverlay.java new file mode 100644 index 000000000..831a53a19 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/DefaultOverlay.java @@ -0,0 +1,62 @@ +package org.apache.maven.plugins.war.overlay; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugins.war.Overlay; + +/** + * A default overlay implementation based on an {@link Artifact}. + * + * @author Stephane Nicoll + */ +public class DefaultOverlay + extends Overlay +{ + + /** + * Creates an overlay for the specified artifact. + * + * @param a the artifact + */ + public DefaultOverlay( Artifact a ) + { + super(); + setGroupId( a.getGroupId() ); + setArtifactId( a.getArtifactId() ); + setClassifier( a.getClassifier() ); + setArtifact( a ); + setType( a.getType() ); + } + + /** + * Creates an overlay for the specified artifact. + * + * @param a the artifact + * @param includes the includes to use + * @param excludes the excludes to use + */ + public DefaultOverlay( Artifact a, String[] includes, String[] excludes ) + { + this( a ); + setIncludes( includes ); + setExcludes( excludes ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/InvalidOverlayConfigurationException.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/InvalidOverlayConfigurationException.java new file mode 100644 index 000000000..3470ef57c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/InvalidOverlayConfigurationException.java @@ -0,0 +1,54 @@ +package org.apache.maven.plugins.war.overlay; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; + +/** + * Thrown if the overlay configuration is invalid. + * + * @author Stephane Nicoll + */ +public class InvalidOverlayConfigurationException + extends MojoExecutionException +{ + + /** + * + */ + private static final long serialVersionUID = -9048144470408031414L; + + /** + * @param string Set the message of the exception. + */ + public InvalidOverlayConfigurationException( String string ) + { + super( string ); + } + + /** + * @param string Set the message of the exception. + * @param throwable {@link Throwable} + */ + public InvalidOverlayConfigurationException( String string, Throwable throwable ) + { + super( string, throwable ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/OverlayManager.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/OverlayManager.java new file mode 100644 index 000000000..cf83d5e29 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/overlay/OverlayManager.java @@ -0,0 +1,253 @@ +package org.apache.maven.plugins.war.overlay; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.project.MavenProject; + +/** + * Manages the overlays. + * + * @author Stephane Nicoll + */ +public class OverlayManager +{ + private final List overlays; + + private final MavenProject project; + + private final List artifactsOverlays; + + /** + * Creates a manager with the specified overlays. + * + * Note that the list is potentially updated by the manager so a new list is created based on the overlays. + * + * @param overlays the overlays + * @param project the maven project + * @param defaultIncludes the default includes to use + * @param defaultExcludes the default excludes to use + * @param currentProjectOverlay the overlay for the current project + * @throws InvalidOverlayConfigurationException if the config is invalid + */ + public OverlayManager( List overlays, MavenProject project, String[] defaultIncludes, + String[] defaultExcludes, Overlay currentProjectOverlay ) + throws InvalidOverlayConfigurationException + { + this.overlays = new ArrayList<>(); + if ( overlays != null ) + { + this.overlays.addAll( overlays ); + } + this.project = project; + + this.artifactsOverlays = getOverlaysAsArtifacts(); + + // Initialize + initialize( defaultIncludes, defaultExcludes, currentProjectOverlay ); + + } + + /** + * Returns the resolved overlays. + * + * @return the overlays + */ + public List getOverlays() + { + return overlays; + } + + /** + * Returns the id of the resolved overlays. + * + * @return the overlay ids + */ + public List getOverlayIds() + { + final List result = new ArrayList<>(); + for ( Overlay overlay : overlays ) + { + result.add( overlay.getId() ); + } + return result; + + } + + /** + * Initializes the manager and validates the overlays configuration. + * + * @param defaultIncludes the default includes to use + * @param defaultExcludes the default excludes to use + * @param currentProjectOverlay the overlay for the current project + * @throws InvalidOverlayConfigurationException if the configuration is invalid + */ + void initialize( String[] defaultIncludes, String[] defaultExcludes, Overlay currentProjectOverlay ) + throws InvalidOverlayConfigurationException + { + + // Build the list of configured artifacts and makes sure that each overlay + // refer to a valid artifact + final List configuredWarArtifacts = new ArrayList<>(); + final ListIterator it = overlays.listIterator(); + while ( it.hasNext() ) + { + Overlay overlay = it.next(); + if ( overlay == null ) + { + throw new InvalidOverlayConfigurationException( "overlay could not be null." ); + } + // If it's the current project, return the project instance + if ( overlay.isCurrentProject() ) + { + overlay = currentProjectOverlay; + it.set( overlay ); + } + // default includes/excludes - only if the overlay uses the default settings + if ( Arrays.equals( Overlay.DEFAULT_INCLUDES, overlay.getIncludes() ) + && Arrays.equals( Overlay.DEFAULT_EXCLUDES, overlay.getExcludes() ) ) + { + overlay.setIncludes( defaultIncludes ); + overlay.setExcludes( defaultExcludes ); + } + + final Artifact artifact = getAssociatedArtifact( overlay ); + if ( artifact != null ) + { + configuredWarArtifacts.add( artifact ); + overlay.setArtifact( artifact ); + } + } + + // Build the list of missing overlays + for ( Artifact artifact : artifactsOverlays ) + { + if ( !configuredWarArtifacts.contains( artifact ) ) + { + // Add a default overlay for the given artifact which will be applied after + // the ones that have been configured + overlays.add( new DefaultOverlay( artifact, defaultIncludes, defaultExcludes ) ); + } + } + + // Final validation, make sure that the current project is in there. Otherwise add it first + for ( Overlay overlay : overlays ) + { + if ( overlay.equals( currentProjectOverlay ) ) + { + return; + } + } + overlays.add( 0, currentProjectOverlay ); + } + + /** + * Returns the Artifact associated to the specified overlay. + * + * If the overlay defines the current project, null is returned. If no artifact could not be found for the + * overlay a InvalidOverlayConfigurationException is thrown. + * + * @param overlay an overlay + * @return the artifact associated to the overlay + * @throws org.apache.maven.plugins.war.overlay.InvalidOverlayConfigurationException if the overlay does not have an + * associated artifact + */ + Artifact getAssociatedArtifact( final Overlay overlay ) + throws InvalidOverlayConfigurationException + { + if ( overlay.isCurrentProject() ) + { + return null; + } + + for ( Artifact artifact : artifactsOverlays ) + { + // Handle classifier dependencies properly (clash management) + if ( compareOverlayWithArtifact( overlay, artifact ) ) + { + return artifact; + } + } + + // maybe its a project dependencies zip or an other type + Set projectArtifacts = this.project.getDependencyArtifacts(); + if ( projectArtifacts != null ) + { + for ( Artifact artifact : projectArtifacts ) + { + if ( compareOverlayWithArtifact( overlay, artifact ) ) + { + return artifact; + } + } + } + // CHECKSTYLE_OFF: LineLength + throw new InvalidOverlayConfigurationException( "overlay [" + overlay + "] is not a dependency of the project." ); + // CHECKSTYLE_ON: LineLength + + } + + /** + * Compare groupId && artifactId && type && classifier. + * + * @param overlay the overlay + * @param artifact the artifact + * @return boolean true if equals + */ + private boolean compareOverlayWithArtifact( Overlay overlay, Artifact artifact ) + { + return ( Objects.equals( overlay.getGroupId(), artifact.getGroupId() ) + && Objects.equals( overlay.getArtifactId(), artifact.getArtifactId() ) + && Objects.equals( overlay.getType(), artifact.getType() ) + // MWAR-241 Make sure to treat null and "" as equal when comparing the classifier + && Objects.equals( Objects.toString( overlay.getClassifier() ), + Objects.toString( artifact.getClassifier() ) ) ); + } + + /** + * Returns a list of WAR {@link org.apache.maven.artifact.Artifact} describing the overlays of the current project. + * + * @return the overlays as artifacts objects + */ + private List getOverlaysAsArtifacts() + { + ScopeArtifactFilter filter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME ); + final Set artifacts = project.getArtifacts(); + + final List result = new ArrayList<>(); + for ( Artifact artifact : artifacts ) + { + if ( !artifact.isOptional() && filter.include( artifact ) && ( "war".equals( artifact.getType() ) ) ) + { + result.add( artifact ); + } + } + return result; + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/AbstractWarPackagingTask.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/AbstractWarPackagingTask.java new file mode 100644 index 000000000..e230f6cf6 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/AbstractWarPackagingTask.java @@ -0,0 +1,492 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.input.XmlStreamReader; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.util.PathSet; +import org.apache.maven.plugins.war.util.WebappStructure; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.apache.maven.shared.mapping.MappingUtils; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.UnArchiver; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; + +/** + * @author Stephane Nicoll + */ +public abstract class AbstractWarPackagingTask + implements WarPackagingTask +{ + /** + * The default list of includes. + */ + public static final String[] DEFAULT_INCLUDES = { "**/**" }; + + /** + * The {@code WEB-INF} path. + */ + public static final String WEB_INF_PATH = "WEB-INF"; + + /** + * The {@code META-INF} path. + */ + public static final String META_INF_PATH = "META-INF"; + + /** + * The {@code classes} path. + */ + public static final String CLASSES_PATH = "WEB-INF/classes/"; + + /** + * The {@code lib} path. + */ + public static final String LIB_PATH = "WEB-INF/lib/"; + + /** + * Copies the files if possible with an optional target prefix. + * + * Copy uses a first-win strategy: files that have already been copied by previous tasks are ignored. This method + * makes sure to update the list of protected files which gives the list of files that have already been copied. + * + * If the structure of the source directory is not the same as the root of the webapp, use the targetPrefix + * parameter to specify in which particular directory the files should be copied. Use null to copy the + * files with the same structure + * + * @param sourceId the source id + * @param context the context to use + * @param sourceBaseDir the base directory from which the sourceFilesSet will be copied + * @param sourceFilesSet the files to be copied + * @param targetPrefix the prefix to add to the target file name + * @param filtered filter or not. + * @throws IOException if an error occurred while copying the files + * @throws MojoExecutionException if an error occurs. + */ + protected void copyFiles( String sourceId, WarPackagingContext context, File sourceBaseDir, PathSet sourceFilesSet, + String targetPrefix, boolean filtered ) + throws IOException, MojoExecutionException + { + for ( String fileToCopyName : sourceFilesSet.paths() ) + { + final File sourceFile = new File( sourceBaseDir, fileToCopyName ); + + String destinationFileName; + if ( targetPrefix == null ) + { + destinationFileName = fileToCopyName; + } + else + { + destinationFileName = targetPrefix + fileToCopyName; + } + + if ( filtered && !context.isNonFilteredExtension( sourceFile.getName() ) ) + { + copyFilteredFile( sourceId, context, sourceFile, destinationFileName ); + } + else + { + copyFile( sourceId, context, sourceFile, destinationFileName ); + } + } + } + + /** + * Copies the files if possible as is. + * + * Copy uses a first-win strategy: files that have already been copied by previous tasks are ignored. This method + * makes sure to update the list of protected files which gives the list of files that have already been copied. + * + * @param sourceId the source id + * @param context the context to use + * @param sourceBaseDir the base directory from which the sourceFilesSet will be copied + * @param sourceFilesSet the files to be copied + * @param filtered filter or not. + * @throws IOException if an error occurred while copying the files + * @throws MojoExecutionException break the build. + */ + protected void copyFiles( String sourceId, WarPackagingContext context, File sourceBaseDir, PathSet sourceFilesSet, + boolean filtered ) + throws IOException, MojoExecutionException + { + copyFiles( sourceId, context, sourceBaseDir, sourceFilesSet, null, filtered ); + } + + /** + * Copy the specified file if the target location has not yet already been used. + * + * The targetFileName is the relative path according to the root of the generated web application. + * + * @param sourceId the source id + * @param context the context to use + * @param file the file to copy + * @param targetFilename the relative path according to the root of the webapp + * @throws IOException if an error occurred while copying + */ + // CHECKSTYLE_OFF: LineLength + protected void copyFile( String sourceId, final WarPackagingContext context, final File file, String targetFilename ) + throws IOException + // CHECKSTYLE_ON: LineLength + { + final File targetFile = new File( context.getWebappDirectory(), targetFilename ); + + if ( file.isFile() ) + { + context.getWebappStructure().registerFile( sourceId, targetFilename, + new WebappStructure.RegistrationCallback() + { + public void registered( String ownerId, String targetFilename ) + throws IOException + { + copyFile( context, file, targetFile, targetFilename, + false ); + } + + public void alreadyRegistered( String ownerId, + String targetFilename ) + throws IOException + { + copyFile( context, file, targetFile, targetFilename, + true ); + } + + public void refused( String ownerId, String targetFilename, + String actualOwnerId ) + throws IOException + { + context.getLog().debug( " - " + + targetFilename + + " wasn't copied because it has " + + "already been packaged for overlay [" + + actualOwnerId + "]." ); + } + + public void superseded( String ownerId, + String targetFilename, + String deprecatedOwnerId ) + throws IOException + { + context.getLog().info( "File [" + + targetFilename + + "] belonged to overlay [" + + deprecatedOwnerId + + "] so it will be overwritten." ); + copyFile( context, file, targetFile, targetFilename, + false ); + } + + public void supersededUnknownOwner( String ownerId, + String targetFilename, + String unknownOwnerId ) + throws IOException + { + // CHECKSTYLE_OFF: LineLength + context.getLog().warn( "File [" + + targetFilename + + "] belonged to overlay [" + + unknownOwnerId + + "] which does not exist anymore in the current project. It is recommended to invoke " + + "clean if the dependencies of the project changed." ); + // CHECKSTYLE_ON: LineLength + copyFile( context, file, targetFile, targetFilename, + false ); + } + } ); + } + else if ( !targetFile.exists() && !targetFile.mkdirs() ) + { + context.getLog().info( "Failed to create directory " + targetFile.getAbsolutePath() ); + } + } + + /** + * Copy the specified file if the target location has not yet already been used and filter its content with the + * configured filter properties. + * + * The targetFileName is the relative path according to the root of the generated web application. + * + * @param sourceId the source id + * @param context the context to use + * @param file the file to copy + * @param targetFilename the relative path according to the root of the webapp + * @return true if the file has been copied, false otherwise + * @throws IOException if an error occurred while copying + * @throws MojoExecutionException if an error occurred while retrieving the filter properties + */ + protected boolean copyFilteredFile( String sourceId, final WarPackagingContext context, File file, + String targetFilename ) + throws IOException, MojoExecutionException + { + context.addResource( targetFilename ); + + if ( context.getWebappStructure().registerFile( sourceId, targetFilename ) ) + { + final File targetFile = new File( context.getWebappDirectory(), targetFilename ); + final String encoding; + try + { + if ( isXmlFile( file ) ) + { + // For xml-files we extract the encoding from the files + encoding = getEncoding( file ); + } + else + { + // For all others we use the configured encoding + encoding = context.getResourceEncoding(); + } + // fix for MWAR-36, ensures that the parent dir are created first + targetFile.getParentFile().mkdirs(); + + context.getMavenFileFilter().copyFile( file, targetFile, true, context.getFilterWrappers(), encoding ); + } + catch ( MavenFilteringException e ) + { + throw new MojoExecutionException( e.getMessage(), e ); + } + // CHECKSTYLE_OFF: LineLength + // Add the file to the protected list + context.getLog().debug( " + " + targetFilename + " has been copied (filtered encoding='" + encoding + "')." ); + // CHECKSTYLE_ON: LineLength + return true; + } + else + { + context.getLog().debug( " - " + targetFilename + + " wasn't copied because it has already been packaged (filtered)." ); + return false; + } + } + + /** + * Unpacks the specified file to the specified directory. + * + * @param context the packaging context + * @param file the file to unpack + * @param unpackDirectory the directory to use for th unpacked file + * @throws MojoExecutionException if an error occurred while unpacking the file + */ + protected void doUnpack( WarPackagingContext context, File file, File unpackDirectory ) + throws MojoExecutionException + { + String archiveExt = FileUtils.getExtension( file.getAbsolutePath() ).toLowerCase(); + + try + { + UnArchiver unArchiver = context.getArchiverManager().getUnArchiver( archiveExt ); + unArchiver.setSourceFile( file ); + unArchiver.setDestDirectory( unpackDirectory ); + unArchiver.setOverwrite( true ); + unArchiver.extract(); + } + catch ( ArchiverException e ) + { + throw new MojoExecutionException( "Error unpacking file [" + file.getAbsolutePath() + "]" + " to [" + + unpackDirectory.getAbsolutePath() + "]", e ); + } + catch ( NoSuchArchiverException e ) + { + context.getLog().warn( "Skip unpacking dependency file [" + file.getAbsolutePath() + + " with unknown extension [" + archiveExt + "]" ); + } + } + + /** + * Copy file from source to destination. The directories up to destination will be created if they + * don't already exist. if the onlyIfModified flag is false, destination will be + * overwritten if it already exists. If the flag is true destination will be overwritten if it's not up to + * date. + * + * @param context the packaging context + * @param source an existing non-directory File to copy bytes from + * @param destination a non-directory File to write bytes to (possibly overwriting). + * @param targetFilename the relative path of the file from the webapp root directory + * @param onlyIfModified if true, copy the file only if the source has changed, always copy otherwise + * @return true if the file has been copied/updated, false otherwise + * @throws IOException if source does not exist, destination cannot be written to, or an + * IO error occurs during copying + */ + protected boolean copyFile( WarPackagingContext context, File source, File destination, String targetFilename, + boolean onlyIfModified ) + throws IOException + { + context.addResource( targetFilename ); + + if ( onlyIfModified && destination.lastModified() >= source.lastModified() ) + { + context.getLog().debug( " * " + targetFilename + " is up to date." ); + return false; + } + else + { + if ( source.isDirectory() ) + { + context.getLog().warn( " + " + targetFilename + " is packaged from the source folder" ); + + try + { + JarArchiver archiver = context.getJarArchiver(); + archiver.addDirectory( source ); + archiver.setDestFile( destination ); + archiver.createArchive(); + } + catch ( ArchiverException e ) + { + String msg = "Failed to create " + targetFilename; + context.getLog().error( msg, e ); + IOException ioe = new IOException( msg ); + ioe.initCause( e ); + throw ioe; + } + } + else + { + FileUtils.copyFile( source.getCanonicalFile(), destination ); + // preserve timestamp + destination.setLastModified( source.lastModified() ); + context.getLog().debug( " + " + targetFilename + " has been copied." ); + } + return true; + } + } + + /** + * Get the encoding from an XML-file. + * + * @param webXml the XML-file + * @return The encoding of the XML-file, or UTF-8 if it's not specified in the file + * @throws java.io.IOException if an error occurred while reading the file + */ + protected String getEncoding( File webXml ) + throws IOException + { + try ( XmlStreamReader xmlReader = new XmlStreamReader( webXml ) ) + { + return xmlReader.getEncoding(); + } + } + + /** + * Returns the file to copy. If the includes are null or empty, the default includes are used. + * + * @param baseDir the base directory to start from + * @param includes the includes + * @param excludes the excludes + * @return the files to copy + */ + protected PathSet getFilesToIncludes( File baseDir, String[] includes, String[] excludes ) + { + return getFilesToIncludes( baseDir, includes, excludes, false ); + } + + /** + * Returns the file to copy. If the includes are null or empty, the default includes are used. + * + * @param baseDir the base directory to start from + * @param includes the includes + * @param excludes the excludes + * @param includeDirectories include directories yes or not. + * @return the files to copy + */ + // CHECKSTYLE_OFF: LineLength + protected PathSet getFilesToIncludes( File baseDir, String[] includes, String[] excludes, boolean includeDirectories ) + // CHECKSTYLE_ON: LineLength + { + final DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( baseDir ); + + if ( excludes != null ) + { + scanner.setExcludes( excludes ); + } + scanner.addDefaultExcludes(); + + if ( includes != null && includes.length > 0 ) + { + scanner.setIncludes( includes ); + } + else + { + scanner.setIncludes( DEFAULT_INCLUDES ); + } + + scanner.scan(); + + PathSet pathSet = new PathSet( scanner.getIncludedFiles() ); + + if ( includeDirectories ) + { + pathSet.addAll( scanner.getIncludedDirectories() ); + } + + return pathSet; + } + + /** + * Returns the final name of the specified artifact. + * + * If the outputFileNameMapping is set, it is used, otherwise the standard naming scheme is used. + * + * @param context the packaging context + * @param artifact the artifact + * @return the converted filename of the artifact + * @throws InterpolationException in case of interpolation problem. + */ + protected String getArtifactFinalName( WarPackagingContext context, Artifact artifact ) + throws InterpolationException + { + if ( context.getOutputFileNameMapping() != null ) + { + return MappingUtils.evaluateFileNameMapping( context.getOutputFileNameMapping(), artifact ); + } + + String classifier = artifact.getClassifier(); + if ( ( classifier != null ) && !( "".equals( classifier.trim() ) ) ) + { + return MappingUtils.evaluateFileNameMapping( MappingUtils.DEFAULT_FILE_NAME_MAPPING_CLASSIFIER, artifact ); + } + else + { + return MappingUtils.evaluateFileNameMapping( MappingUtils.DEFAULT_FILE_NAME_MAPPING, artifact ); + } + + } + + /** + * Returns true if the File-object is a file (not a directory) that is not + * null and has a file name that ends in ".xml". + * + * @param file The file to check + * @return true if the file is an xml-file, otherwise false + * @since 2.3 + */ + private boolean isXmlFile( File file ) + { + return file != null && file.isFile() && file.getName().endsWith( ".xml" ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ArtifactsPackagingTask.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ArtifactsPackagingTask.java new file mode 100644 index 000000000..2fa484d6d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ArtifactsPackagingTask.java @@ -0,0 +1,187 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.Overlay; +import org.codehaus.plexus.interpolation.InterpolationException; + +/** + * Handles the artifacts that needs to be packaged in the web application. + * + * @author Stephane Nicoll + */ +public class ArtifactsPackagingTask + extends AbstractWarPackagingTask +{ + + /** + * The {@code tld} path. + */ + public static final String TLD_PATH = "WEB-INF/tld/"; + + /** + * The {@code services} path. + */ + public static final String SERVICES_PATH = "WEB-INF/services/"; + + /** + * The {@code modules} path. + */ + public static final String MODULES_PATH = "WEB-INF/modules/"; + + /** + * The {@code extensions} path. + */ + public static final String EXTENSIONS_PATH = "WEB-INF/extensions/"; + + private final Set artifacts; + + private final String id; + + /** + * @param artifacts {@link #artifacts} + * @param currentProjectOverlay {@link #id} + */ + public ArtifactsPackagingTask( Set artifacts, Overlay currentProjectOverlay ) + { + this.artifacts = artifacts; + this.id = currentProjectOverlay.getId(); + } + + @Override + public void performPackaging( WarPackagingContext context ) + throws MojoExecutionException + { + try + { + final ScopeArtifactFilter filter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME ); + final List duplicates = findDuplicates( context, artifacts ); + + for ( Artifact artifact : artifacts ) + { + String targetFileName = getArtifactFinalName( context, artifact ); + + context.getLog().debug( "Processing: " + targetFileName ); + + if ( duplicates.contains( targetFileName ) ) + { + context.getLog().debug( "Duplicate found: " + targetFileName ); + targetFileName = artifact.getGroupId() + "-" + targetFileName; + context.getLog().debug( "Renamed to: " + targetFileName ); + } + context.getWebappStructure().registerTargetFileName( artifact, targetFileName ); + + if ( !artifact.isOptional() && filter.include( artifact ) ) + { + try + { + String type = artifact.getType(); + if ( "tld".equals( type ) ) + { + copyFile( id, context, artifact.getFile(), TLD_PATH + targetFileName ); + } + else if ( "aar".equals( type ) ) + { + copyFile( id, context, artifact.getFile(), SERVICES_PATH + targetFileName ); + } + else if ( "mar".equals( type ) ) + { + copyFile( id, context, artifact.getFile(), MODULES_PATH + targetFileName ); + } + else if ( "xar".equals( type ) ) + { + copyFile( id, context, artifact.getFile(), EXTENSIONS_PATH + targetFileName ); + } + else if ( "jar".equals( type ) || "ejb".equals( type ) || "ejb-client".equals( type ) + || "test-jar".equals( type ) || "bundle".equals( type ) ) + { + copyFile( id, context, artifact.getFile(), LIB_PATH + targetFileName ); + } + else if ( "par".equals( type ) ) + { + targetFileName = targetFileName.substring( 0, targetFileName.lastIndexOf( '.' ) ) + ".jar"; + copyFile( id, context, artifact.getFile(), LIB_PATH + targetFileName ); + } + else if ( "war".equals( type ) ) + { + // Nothing to do here, it is an overlay and it's already handled + context.getLog().debug( "war artifacts are handled as overlays, ignoring [" + artifact + + "]" ); + } + else if ( "zip".equals( type ) ) + { + // Nothing to do here, it is an overlay and it's already handled + context.getLog().debug( "zip artifacts are handled as overlays, ignoring [" + artifact + + "]" ); + } + else + { + context.getLog().debug( "Artifact of type [" + type + "] is not supported, ignoring [" + + artifact + "]" ); + } + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to copy file for artifact [" + artifact + "]", e ); + } + } + } + } + catch ( InterpolationException e ) + { + throw new MojoExecutionException( e.getMessage(), e ); + } + } + + /** + * Searches a set of artifacts for duplicate filenames and returns a list of duplicates. + * + * @param context the packaging context + * @param artifacts set of artifacts + * @return List of duplicated artifacts as bundling file names + */ + private List findDuplicates( WarPackagingContext context, Set artifacts ) + throws InterpolationException + { + List duplicates = new ArrayList<>(); + List identifiers = new ArrayList<>(); + for ( Artifact artifact : artifacts ) + { + String candidate = getArtifactFinalName( context, artifact ); + if ( identifiers.contains( candidate ) ) + { + duplicates.add( candidate ); + } + else + { + identifiers.add( candidate ); + } + } + return duplicates; + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ClassesPackagingTask.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ClassesPackagingTask.java new file mode 100644 index 000000000..ea136a695 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/ClassesPackagingTask.java @@ -0,0 +1,128 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.plugins.war.util.ClassesPackager; +import org.apache.maven.plugins.war.util.PathSet; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.interpolation.InterpolationException; + +import java.io.File; +import java.io.IOException; + +/** + * Handles the classes directory that needs to be packaged in the web application. + * + * Based on the {@link WarPackagingContext#archiveClasses()} flag, the resources are either copied into to + * WEB-INF/classes directory or archived in a jar within the WEB-INF/lib directory. + * + * @author Stephane Nicoll + */ +public class ClassesPackagingTask + extends AbstractWarPackagingTask +{ + private final Overlay currentProjectOverlay; + + /** + * @param currentProjectOverlay {@link #currentProjectOverlay} + */ + public ClassesPackagingTask( Overlay currentProjectOverlay ) + { + this.currentProjectOverlay = currentProjectOverlay; + } + + @Override + public void performPackaging( WarPackagingContext context ) + throws MojoExecutionException + { + final File webappClassesDirectory = new File( context.getWebappDirectory(), CLASSES_PATH ); + if ( !webappClassesDirectory.exists() ) + { + webappClassesDirectory.mkdirs(); + } + + if ( context.getClassesDirectory().exists() && !context.getClassesDirectory().equals( webappClassesDirectory ) ) + { + if ( context.archiveClasses() ) + { + generateJarArchive( context ); + } + else + { + final PathSet sources = getFilesToIncludes( context.getClassesDirectory(), null, null ); + try + { + copyFiles( currentProjectOverlay.getId(), context, context.getClassesDirectory(), sources, + CLASSES_PATH, false ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not copy webapp classes [" + + context.getClassesDirectory().getAbsolutePath() + "]", e ); + } + } + } + } + + /** + * @param context The warPackingContext. + * @throws MojoExecutionException In case of an error. + */ + protected void generateJarArchive( WarPackagingContext context ) + throws MojoExecutionException + { + MavenProject project = context.getProject(); + ArtifactFactory factory = context.getArtifactFactory(); + Artifact artifact = + factory.createBuildArtifact( project.getGroupId(), project.getArtifactId(), project.getVersion(), "jar" ); + String archiveName; + try + { + archiveName = getArtifactFinalName( context, artifact ); + } + catch ( InterpolationException e ) + { + throw new MojoExecutionException( "Could not get the final name of the artifact [" + artifact.getGroupId() + + ":" + artifact.getArtifactId() + ":" + artifact.getVersion() + "]", e ); + } + final String targetFilename = LIB_PATH + archiveName; + + if ( context.getWebappStructure().registerFile( currentProjectOverlay.getId(), targetFilename ) ) + { + context.addResource( targetFilename ); + + final File libDirectory = new File( context.getWebappDirectory(), LIB_PATH ); + final File jarFile = new File( libDirectory, archiveName ); + final ClassesPackager packager = new ClassesPackager(); + packager.packageClasses( context.getClassesDirectory(), jarFile, context.getJarArchiver(), + context.getSession(), project, context.getArchive(), + context.getOutputTimestamp() ); + } + else + { + context.getLog().warn( "Could not generate archive classes file [" + targetFilename + + "] has already been copied." ); + } + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/CopyUserManifestTask.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/CopyUserManifestTask.java new file mode 100644 index 000000000..217c18a2d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/CopyUserManifestTask.java @@ -0,0 +1,77 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.logging.SystemStreamLog; + +/** + * @author Haikal Saadh + * + */ +public class CopyUserManifestTask + extends AbstractWarPackagingTask +{ + + /** Instance logger */ + private Log log; + + public Log getLog() + { + if ( log == null ) + { + log = new SystemStreamLog(); + } + return log; + } + + public void setLog( Log log ) + { + this.log = log; + } + + public void performPackaging( WarPackagingContext context ) + throws MojoExecutionException, MojoFailureException + { + File userManifest = context.getArchive().getManifestFile(); + if ( userManifest != null ) + { + + try + { + getLog().info( "Copying manifest..." ); + File metainfDir = new File( context.getWebappDirectory(), META_INF_PATH ); + copyFile( context, userManifest, new File( metainfDir, "MANIFEST.MF" ), "META-INF/MANIFEST.MF", true ); + + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error copying user manifest", e ); + } + } + + } + +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/OverlayPackagingTask.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/OverlayPackagingTask.java new file mode 100644 index 000000000..df7f2b400 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/OverlayPackagingTask.java @@ -0,0 +1,157 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.plugins.war.util.PathSet; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.io.IOException; + +/** + * Handles an overlay. + * + * @author Stephane Nicoll + */ +public class OverlayPackagingTask + extends AbstractWarPackagingTask +{ + private final Overlay overlay; + + /** + * @param overlay {@link #overlay} + * @param currentProjectOverlay current overlay. + */ + public OverlayPackagingTask( Overlay overlay, Overlay currentProjectOverlay ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay could not be null." ); + } + if ( overlay.equals( currentProjectOverlay ) ) + { + throw new IllegalStateException( "Could not handle the current project with this task." ); + } + this.overlay = overlay; + } + + @Override + public void performPackaging( WarPackagingContext context ) + throws MojoExecutionException + { + context.getLog().debug( "OverlayPackagingTask performPackaging overlay.getTargetPath() " + + overlay.getTargetPath() ); + if ( overlay.shouldSkip() ) + { + context.getLog().info( "Skipping overlay [" + overlay + "]" ); + } + else + { + try + { + context.getLog().info( "Processing overlay [" + overlay + "]" ); + + // Step1: Extract if necessary + final File tmpDir = unpackOverlay( context, overlay ); + + // Step2: setup + final PathSet includes = getFilesToIncludes( tmpDir, overlay.getIncludes(), overlay.getExcludes() ); + + // Copy + if ( null == overlay.getTargetPath() ) + { + copyFiles( overlay.getId(), context, tmpDir, includes, overlay.isFiltered() ); + } + else + { + // overlay.getTargetPath() must ended with / + // if not we add it + String targetPath = overlay.getTargetPath(); + if ( !targetPath.endsWith( "/" ) ) + { + targetPath = targetPath + "/"; + } + copyFiles( overlay.getId(), context, tmpDir, includes, targetPath, overlay.isFiltered() ); + } + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to copy file for overlay [" + overlay + "]", e ); + } + } + } + + /** + * Unpacks the specified overlay. + * + * Makes sure to skip the unpack process if the overlay has already been unpacked. + * + * @param context the packaging context + * @param overlay the overlay + * @return the directory containing the unpacked overlay + * @throws MojoExecutionException if an error occurred while unpacking the overlay + */ + protected File unpackOverlay( WarPackagingContext context, Overlay overlay ) + throws MojoExecutionException + { + final File tmpDir = getOverlayTempDirectory( context, overlay ); + + // TODO: not sure it's good, we should reuse the markers of the dependency plugin + if ( FileUtils.sizeOfDirectory( tmpDir ) == 0 + || overlay.getArtifact().getFile().lastModified() > tmpDir.lastModified() ) + { + doUnpack( context, overlay.getArtifact().getFile(), tmpDir ); + } + else + { + context.getLog().debug( "Overlay [" + overlay + "] was already unpacked" ); + } + return tmpDir; + } + + /** + * Returns the directory to use to unpack the specified overlay. + * + * @param context the packaging context + * @param overlay the overlay + * @return the temp directory for the overlay + */ + protected File getOverlayTempDirectory( WarPackagingContext context, Overlay overlay ) + { + final File groupIdDir = new File( context.getOverlaysWorkDirectory(), overlay.getGroupId() ); + if ( !groupIdDir.exists() ) + { + groupIdDir.mkdir(); + } + String directoryName = overlay.getArtifactId(); + if ( overlay.getClassifier() != null ) + { + directoryName = directoryName + "-" + overlay.getClassifier(); + } + final File result = new File( groupIdDir, directoryName ); + if ( !result.exists() ) + { + result.mkdirs(); + } + return result; + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingContext.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingContext.java new file mode 100644 index 000000000..29336a234 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingContext.java @@ -0,0 +1,253 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.util.List; + +import org.apache.maven.archiver.MavenArchiveConfiguration; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.war.util.WebappStructure; +import org.apache.maven.project.MavenProject; +import org.apache.maven.shared.filtering.MavenFileFilter; +import org.apache.maven.shared.utils.io.FileUtils.FilterWrapper; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.archiver.manager.ArchiverManager; + +/** + * The packaging context. + * + * @author Stephane Nicoll + */ +public interface WarPackagingContext +{ + /** + * Returns the maven project. + * + * @return the project + */ + MavenProject getProject(); + + /** + * Returns the webapp directory. Packaging tasks should use this directory to generate the webapp. + * + * @return the webapp directory + */ + File getWebappDirectory(); + + /** + * Returns the main webapp source directory. + * + * @return the webapp source directory + */ + File getWebappSourceDirectory(); + + /** + * Returns the webapp source includes. + * + * @return the webapp source includes + */ + String[] getWebappSourceIncludes(); + + /** + * Returns {@code true} if empty directories should be includes, otherwise {@code false} + * + * @return {@code true} if empty directories should be includes, otherwise {@code false} + */ + boolean isWebappSourceIncludeEmptyDirectories(); + + /** + * Returns the webapp source excludes. + * + * @return the webapp source excludes + */ + String[] getWebappSourceExcludes(); + + /** + * Returns the directory holding generated classes. + * + * @return the classes directory + */ + File getClassesDirectory(); + + /** + * Specify whether the classes resources should be archived in the WEB-INF/lib of the generated web app. + * + * @return true if the classes should be archived, false otherwise + */ + boolean archiveClasses(); + + /** + * Returns the logger to use to output logging event. + * + * @return the logger + */ + Log getLog(); + + /** + * Returns the directory to unpack dependent WARs into if needed. + * + * @return the overlays work directory + */ + File getOverlaysWorkDirectory(); + + /** + * Returns the archiver manager to use. + * + * @return the archiver manager + */ + ArchiverManager getArchiverManager(); + + /** + * The maven archive configuration to use. + * + * @return the maven archive configuration + */ + MavenArchiveConfiguration getArchive(); + + /** + * Returns the Jar archiver needed for archiving classes directory into jar file under WEB-INF/lib. + * + * @return the jar archiver to user + */ + JarArchiver getJarArchiver(); + + /** + * Returns the output file name mapping to use, if any. Returns null if no file name mapping is set. + * + * @return the output file name mapping or null + */ + String getOutputFileNameMapping(); + + /** + * Returns the list of filter files to use. + * + * @return a list of filter files + */ + List getFilters(); + + /** + * Returns the {@link WebappStructure}. + * + * @return the webapp structure + */ + WebappStructure getWebappStructure(); + + /** + * Returns the list of registered overlays for this session. + * + * @return the list of registered overlays, including the current project + */ + List getOwnerIds(); + + /** + * Returns the {@link MavenFileFilter} instance to use. + * + * @return the maven file filter to use + * @since 2.1-alpha-2 + */ + MavenFileFilter getMavenFileFilter(); + + /** + * @return {@link List} of {@link FilterWrapper} + * @since 2.1-alpha-2 + */ + List getFilterWrappers(); + + /** + * Specify if the given fileName belongs to the list of extensions that must not be filtered + * + * @param fileName the name of file + * @return true if it should not be filtered, false otherwise + * @since 2.1-alpha-2 + */ + boolean isNonFilteredExtension( String fileName ); + + /** + * @return filtering deployment descriptor. + */ + boolean isFilteringDeploymentDescriptors(); + + /** + * @return {@link ArtifactFactory} + */ + ArtifactFactory getArtifactFactory(); + + /** + * Returns the Maven session. + * + * @return the Maven session + * @since 2.2 + */ + MavenSession getSession(); + + /** + * Returns the encoding to use for resources. + * + * @return the resource encoding + * @since 2.3 + */ + String getResourceEncoding(); + + /** + * @return to use jvmChmod rather than forking chmod cli + * @since 2.4 + */ + boolean isUseJvmChmod(); + + /** + * Returns the flag that switch on/off the missing web.xml validation + * + * @return failOnMissingWebXml + */ + Boolean isFailOnMissingWebXml(); + + /** + * Add a live resource to the war. + * Used to keep track of existing resources and all copied files. + * All others are outdated and will be removed. + * This prevent calling mvn clean when resources are removed. + * + * @param resource the resource that is to me marked as not outdated + * @since 3.3.0 + * @see #deleteOutdatedResources() + */ + void addResource( String resource ); + + /** + * Delete outdated resources, ie resources that are found in the war but that were not added by the current + * packaging process, then are supposed to be content from a previous run. + * This prevent calling mvn clean when resources are removed. + * + * @since 3.3.0 + * @see #addResource + */ + void deleteOutdatedResources(); + + /** + * Output timestamp for reproducible archive creation. + * + * @return the output timestamp (may be null) + * @since 3.3.0 + */ + String getOutputTimestamp(); +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingTask.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingTask.java new file mode 100644 index 000000000..4260e1deb --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarPackagingTask.java @@ -0,0 +1,45 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; + +/** + * The base packaging task. + * + * @author Stephane Nicoll + */ +public interface WarPackagingTask +{ + + /** + * Performs the packaging for the specified task. + * + * The task is responsible to update the packaging context, namely with the files that have been copied. + * + * @param context the packaging context + * @throws MojoExecutionException if an error occurred + * @throws MojoFailureException if the project configuration is invalid + */ + void performPackaging( WarPackagingContext context ) + throws MojoExecutionException, MojoFailureException; + +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarProjectPackagingTask.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarProjectPackagingTask.java new file mode 100644 index 000000000..599370629 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/packaging/WarProjectPackagingTask.java @@ -0,0 +1,381 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.plugins.war.util.PathSet; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.StringUtils; + +/** + * Handles the project own resources, that is: + *

    + *
  • The list of web resources, if any
  • + *
  • The content of the webapp directory if it exists
  • + *
  • The custom deployment descriptor(s), if any
  • + *
  • The content of the classes directory if it exists
  • + *
  • The dependencies of the project
  • + *
+ * + * @author Stephane Nicoll + */ +public class WarProjectPackagingTask + extends AbstractWarPackagingTask +{ + private final Resource[] webResources; + + private final File webXml; + + private final File containerConfigXML; + + private final String id; + + private Overlay currentProjectOverlay; + + /** + * @param webResources {@link #webResources} + * @param webXml {@link #webXml} + * @param containerConfigXml {@link #containerConfigXML} + * @param currentProjectOverlay {@link #currentProjectOverlay} + */ + public WarProjectPackagingTask( Resource[] webResources, File webXml, File containerConfigXml, + Overlay currentProjectOverlay ) + { + if ( webResources != null ) + { + this.webResources = webResources; + } + else + { + this.webResources = new Resource[0]; + } + this.webXml = webXml; + this.containerConfigXML = containerConfigXml; + this.currentProjectOverlay = currentProjectOverlay; + this.id = currentProjectOverlay.getId(); + } + + @Override + public void performPackaging( WarPackagingContext context ) + throws MojoExecutionException, MojoFailureException + { + context.getLog().info( "Processing war project" ); + + // Prepare the INF directories + File webinfDir = new File( context.getWebappDirectory(), WEB_INF_PATH ); + webinfDir.mkdirs(); + File metainfDir = new File( context.getWebappDirectory(), META_INF_PATH ); + metainfDir.mkdirs(); + + handleWebResources( context ); + + handleWebAppSourceDirectory( context ); + + // Debug mode: dump the path set for the current build + PathSet pathSet = context.getWebappStructure().getStructure( "currentBuild" ); + context.getLog().debug( "Dump of the current build pathSet content -->" ); + for ( String path : pathSet ) + { + context.getLog().debug( path ); + } + context.getLog().debug( "-- end of dump --" ); + + handleDeploymentDescriptors( context, webinfDir, metainfDir, context.isFailOnMissingWebXml() ); + + handleClassesDirectory( context ); + + handleArtifacts( context ); + + if ( !context.getWebappDirectory().mkdirs() ) + { + context.deleteOutdatedResources(); + } + } + + /** + * Handles the web resources. + * + * @param context the packaging context + * @throws MojoExecutionException if a resource could not be copied + */ + protected void handleWebResources( WarPackagingContext context ) + throws MojoExecutionException + { + for ( Resource resource : webResources ) + { + + // MWAR-246 + if ( resource.getDirectory() == null ) + { + throw new MojoExecutionException( "The tag is missing from the tag." ); + } + + if ( !( new File( resource.getDirectory() ) ).isAbsolute() ) + { + resource.setDirectory( context.getProject().getBasedir() + File.separator + resource.getDirectory() ); + } + + // Make sure that the resource directory is not the same as the webappDirectory + if ( !resource.getDirectory().equals( context.getWebappDirectory().getPath() ) ) + { + + try + { + copyResources( context, resource ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not copy resource [" + resource.getDirectory() + "]", e ); + } + } + } + } + + /** + * Handles the webapp sources. + * + * @param context the packaging context + * @throws MojoExecutionException if the sources could not be copied + */ + protected void handleWebAppSourceDirectory( WarPackagingContext context ) + throws MojoExecutionException + { + // CHECKSTYLE_OFF: LineLength + if ( !context.getWebappSourceDirectory().exists() ) + { + context.getLog().debug( "webapp sources directory does not exist - skipping." ); + } + else if ( !context.getWebappSourceDirectory().getAbsolutePath().equals( context.getWebappDirectory().getPath() ) ) + { + context.getLog().info( "Copying webapp resources [" + context.getWebappSourceDirectory() + "]" ); + final PathSet sources = + getFilesToIncludes( context.getWebappSourceDirectory(), context.getWebappSourceIncludes(), + context.getWebappSourceExcludes(), context.isWebappSourceIncludeEmptyDirectories() ); + + try + { + copyFiles( id, context, context.getWebappSourceDirectory(), sources, false ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not copy webapp sources [" + + context.getWebappDirectory().getAbsolutePath() + "]", e ); + } + } + // CHECKSTYLE_ON: LineLength + } + + /** + * Handles the webapp artifacts. + * + * @param context the packaging context + * @throws MojoExecutionException if the artifacts could not be packaged + */ + protected void handleArtifacts( WarPackagingContext context ) + throws MojoExecutionException + { + ArtifactsPackagingTask task = + new ArtifactsPackagingTask( context.getProject().getArtifacts(), currentProjectOverlay ); + task.performPackaging( context ); + } + + /** + * Handles the webapp classes. + * + * @param context the packaging context + * @throws MojoExecutionException if the classes could not be packaged + */ + protected void handleClassesDirectory( WarPackagingContext context ) + throws MojoExecutionException + { + ClassesPackagingTask task = new ClassesPackagingTask( currentProjectOverlay ); + task.performPackaging( context ); + } + + /** + * Handles the deployment descriptors, if specified. Note that the behavior here is slightly different since the + * customized entry always win, even if an overlay has already packaged a web.xml previously. + * + * @param context the packaging context + * @param webinfDir the web-inf directory + * @param metainfDir the meta-inf directory + * @param failOnMissingWebXml if build should fail if web.xml is not found + * @throws MojoFailureException if the web.xml is specified but does not exist and failOnMissingWebXml is true + * @throws MojoExecutionException if an error occurred while copying the descriptors + */ + protected void handleDeploymentDescriptors( WarPackagingContext context, File webinfDir, File metainfDir, + Boolean failOnMissingWebXml ) + throws MojoFailureException, MojoExecutionException + { + try + { + if ( webXml != null && StringUtils.isNotEmpty( webXml.getName() ) ) + { + if ( !webXml.exists() + && ( failOnMissingWebXml == null || Boolean.TRUE.equals( failOnMissingWebXml ) ) ) + { + throw new MojoFailureException( "The specified web.xml file '" + webXml + "' does not exist" ); + } + + // Making sure that it won't get overlayed + context.getWebappStructure().registerFileForced( id, WEB_INF_PATH + "/web.xml" ); + + if ( context.isFilteringDeploymentDescriptors() ) + { + context.getMavenFileFilter().copyFile( webXml, new File( webinfDir, "web.xml" ), true, + context.getFilterWrappers(), getEncoding( webXml ) ); + } + else + { + copyFile( context, webXml, new File( webinfDir, "web.xml" ), "WEB-INF/web.xml", true ); + } + } + else + { + // the webXml can be the default one + File defaultWebXml = new File( context.getWebappSourceDirectory(), WEB_INF_PATH + "/web.xml" ); + // if exists we can filter it + if ( defaultWebXml.exists() && context.isFilteringDeploymentDescriptors() ) + { + context.getWebappStructure().registerFile( id, WEB_INF_PATH + "/web.xml" ); + context.getMavenFileFilter().copyFile( defaultWebXml, new File( webinfDir, "web.xml" ), true, + context.getFilterWrappers(), getEncoding( defaultWebXml ) ); + } + } + + if ( containerConfigXML != null && StringUtils.isNotEmpty( containerConfigXML.getName() ) ) + { + String xmlFileName = containerConfigXML.getName(); + + context.getWebappStructure().registerFileForced( id, META_INF_PATH + "/" + xmlFileName ); + + if ( context.isFilteringDeploymentDescriptors() ) + { + context.getMavenFileFilter().copyFile( containerConfigXML, new File( metainfDir, xmlFileName ), + true, context.getFilterWrappers(), + getEncoding( containerConfigXML ) ); + } + else + { + copyFile( context, containerConfigXML, new File( metainfDir, xmlFileName ), "META-INF/" + + xmlFileName, true ); + } + } + } + catch ( IOException e ) + { + if ( failOnMissingWebXml == null || Boolean.TRUE.equals( failOnMissingWebXml ) ) + { + throw new MojoExecutionException( "Failed to copy deployment descriptor", e ); + } + } + catch ( MavenFilteringException e ) + { + throw new MojoExecutionException( "Failed to copy deployment descriptor", e ); + } + } + + /** + * Copies webapp webResources from the specified directory. + * + * @param context the WAR packaging context to use + * @param resource the resource to copy + * @throws IOException if an error occurred while copying the resources + * @throws MojoExecutionException if an error occurred while retrieving the filter properties + */ + public void copyResources( WarPackagingContext context, Resource resource ) + throws IOException, MojoExecutionException + { + if ( !context.getWebappDirectory().exists() ) + { + context.getLog().warn( "Not copying webapp webResources [" + resource.getDirectory() + + "]: webapp directory [" + context.getWebappDirectory().getAbsolutePath() + + "] does not exist!" ); + } + + context.getLog().info( "Copying webapp webResources [" + resource.getDirectory() + "] to [" + + context.getWebappDirectory().getAbsolutePath() + "]" ); + String[] fileNames = getFilesToCopy( resource ); + for ( String fileName : fileNames ) + { + String targetFileName = fileName; + if ( resource.getTargetPath() != null ) + { + // TODO make sure this thing is 100% safe + // MWAR-129 if targetPath is only a dot . or ./ + // and the Resource is in a part of the warSourceDirectory the file from sources will override this + // that's we don't have to add the targetPath yep not nice but works + if ( !Objects.equals( ".", resource.getTargetPath() ) + && !Objects.equals( "./", resource.getTargetPath() ) ) + { + targetFileName = resource.getTargetPath() + File.separator + targetFileName; + } + } + if ( resource.isFiltering() && !context.isNonFilteredExtension( fileName ) ) + { + copyFilteredFile( id, context, new File( resource.getDirectory(), fileName ), targetFileName ); + } + else + { + copyFile( id, context, new File( resource.getDirectory(), fileName ), targetFileName ); + } + } + } + + /** + * Returns a list of filenames that should be copied over to the destination directory. + * + * @param resource the resource to be scanned + * @return the array of filenames, relative to the sourceDir + */ + private String[] getFilesToCopy( Resource resource ) + { + // CHECKSTYLE_OFF: LineLength + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( resource.getDirectory() ); + if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() ) + { + scanner.setIncludes( resource.getIncludes().toArray( new String[resource.getIncludes().size()] ) ); + } + else + { + scanner.setIncludes( DEFAULT_INCLUDES ); + } + if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() ) + { + scanner.setExcludes( resource.getExcludes().toArray( new String[resource.getExcludes().size()] ) ); + } + + scanner.addDefaultExcludes(); + + scanner.scan(); + + return scanner.getIncludedFiles(); + // CHECKSTYLE_ON: LineLength + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/ClassesPackager.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/ClassesPackager.java new file mode 100644 index 000000000..2b14617cf --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/ClassesPackager.java @@ -0,0 +1,88 @@ +package org.apache.maven.plugins.war.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.archiver.MavenArchiveConfiguration; +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.packaging.AbstractWarPackagingTask; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.archiver.jar.ManifestException; + +import java.io.File; +import java.io.IOException; + +/** + * Packages the content of the classes directory. + * + * @author Stephane Nicoll + */ +public class ClassesPackager +{ + + /** + * Package the classes + * + * @param classesDirectory the classes directory + * @param targetFile the target file + * @param jarArchiver the jar archiver to use + * @param session the current session + * @param project the related project + * @param archiveConfiguration the archive configuration to use + * @param outputTimestamp the output timestamp for reproducibility + * @throws MojoExecutionException if an error occurred while creating the archive + */ + public void packageClasses( File classesDirectory, File targetFile, JarArchiver jarArchiver, MavenSession session, + MavenProject project, MavenArchiveConfiguration archiveConfiguration, + String outputTimestamp ) + throws MojoExecutionException + { + + try + { + final MavenArchiver archiver = new MavenArchiver(); + archiver.setArchiver( jarArchiver ); + archiver.setOutputFile( targetFile ); + archiver.setCreatedBy( "Maven WAR Plugin", "org.apache.maven.plugins", "maven-war-plugin" ); + archiver.configureReproducible( outputTimestamp ); + archiver.getArchiver().addDirectory( classesDirectory ); + archiver.createArchive( session, project, archiveConfiguration ); + } + catch ( ArchiverException | ManifestException | IOException | DependencyResolutionRequiredException e ) + { + throw new MojoExecutionException( "Could not create classes archive", e ); + } + } + + /** + * Returns the classes directory from the specified webapp directory. + * + * @param webappDirectory the webapp directory + * @return the classes directory of the specified webapp directory + */ + public File getClassesDirectory( File webappDirectory ) + { + return new File( webappDirectory, AbstractWarPackagingTask.CLASSES_PATH ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/DependencyInfo.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/DependencyInfo.java new file mode 100644 index 000000000..f38ab060a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/DependencyInfo.java @@ -0,0 +1,103 @@ +package org.apache.maven.plugins.war.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.Objects; + +import org.apache.maven.model.Dependency; + +/** + * Holds a dependency and packaging information. + * + * @author Stephane Nicoll + */ +public class DependencyInfo +{ + + private final Dependency dependency; + + private String targetFileName; + + /** + * Creates a new instance. + * + * @param dependency the dependency + */ + public DependencyInfo( Dependency dependency ) + { + this.dependency = dependency; + } + + /** + * Returns the dependency. + * + * @return the dependency + */ + public Dependency getDependency() + { + return dependency; + } + + /** + * Returns the target filename of the dependency. If no target file name is associated, returns null. + * + * @return the target file name or null + */ + public String getTargetFileName() + { + return targetFileName; + } + + /** + * Sets the target file name. + * + * @param targetFileName the target file name + */ + public void setTargetFileName( String targetFileName ) + { + this.targetFileName = targetFileName; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + DependencyInfo that = (DependencyInfo) o; + + return Objects.equals( dependency, that.dependency ); + } + + @Override + public int hashCode() + { + int result; + result = ( dependency != null ? dependency.hashCode() : 0 ); + result = 31 * result + ( targetFileName != null ? targetFileName.hashCode() : 0 ); + return result; + } +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/PathSet.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/PathSet.java new file mode 100644 index 000000000..23da7b377 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/PathSet.java @@ -0,0 +1,264 @@ +package org.apache.maven.plugins.war.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.codehaus.plexus.util.DirectoryScanner; + +import java.io.File; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Set of file's paths. + * + * The class extends functionality of a "normal" set of strings by a process of the paths normalization. All paths are + * converted to unix form (slashes) and they don't start with starting /. + * + * @author Piotr Tabor + */ + +public class PathSet + implements Iterable +{ + private static final String SEPARATOR = "/"; + private static final char SEPARATOR_CHAR = SEPARATOR.charAt( 0 ); + /** + * Set of normalized paths + */ + private Set pathsSet = new LinkedHashSet<>(); + + static String normalizeSubPath( String path ) + { + if ( path.isEmpty() ) + { + return path; + } + String cleanPath = path.replaceAll( "[\\\\]+", SEPARATOR ) + .replaceAll( "[/]+" , SEPARATOR ); + cleanPath = cleanPath.charAt( 0 ) == SEPARATOR_CHAR ? cleanPath.substring( 1 ) : cleanPath; + if ( cleanPath.isEmpty() ) + { + return cleanPath; + } + if ( cleanPath.charAt( cleanPath.length() - 1 ) == SEPARATOR_CHAR ) + { + return cleanPath.substring( 0, cleanPath.length() - 1 ); + } + return cleanPath; + } + + /*-------------------- Business interface ------------------------------*/ + + /** + * Creates an empty paths set + */ + public PathSet() + { + /* Empty default constructor */ + } + + /** + * Creates paths set and normalizate and adds all 'paths'. The source 'paths' will not be changed + * + * @param paths to be added + */ + public PathSet( Collection paths ) + { + addAll( paths ); + } + + /** + * Creates paths set and normalizate and adds all 'paths'. The source 'paths' will not be changed + * + * @param paths to be added + */ + public PathSet( String[] paths ) + { + addAll( paths ); + } + + /** + * Normalizes and adds given path to the set. + * + * @param path to be added + */ + public void add( String path ) + { + pathsSet.add( normalizeSubPath( path ) ); + } + + /** + * Normalizes and adds given paths (collection of strings) to the set. The source collection will not be changed + * + * @param paths - collection of strings to be added + * @param prefix added to all given paths + */ + public void addAll( Collection paths, String prefix ) + { + for ( String val : paths ) + { + add( prefix + SEPARATOR + val ); + } + } + + /** + * Normalizes and adds given paths to the set. The source collection will not be changed + * + * @param paths to be added + * @param prefix added to all given paths + */ + public void addAll( String[] paths, String prefix ) + { + for ( String val : paths ) + { + add( prefix + SEPARATOR + val ); + } + } + + /** + * Adds given paths to the set. The source collection will not be changed + * + * @param paths to be added + * @param prefix added to all given paths + */ + public void addAll( PathSet paths, String prefix ) + { + for ( String path : paths ) + { + add( prefix + SEPARATOR + path ); + } + } + + /** + * Normalizes and adds given paths (collection of strings) to the set. The source collection will not be changed + * + * @param paths - collection of strings to be added + */ + public void addAll( Collection paths ) + { + addAll( paths, "" ); + } + + /** + * Normalizes and adds given paths to the set. The source collection will not be changed + * + * @param paths to be added + */ + public void addAll( String[] paths ) + { + addAll( paths, "" ); + } + + /** + * Adds given paths to the set. The source collection will not be changed + * + * @param paths to be added + */ + public void addAll( PathSet paths ) + { + addAll( paths, "" ); + } + + /** + * Checks if the set constains given path. The path is normalized before check. + * + * @param path we are looking for in the set. + * @return information if the set constains the path. + */ + public boolean contains( String path ) + { + return pathsSet.contains( normalizeSubPath( path ) ); + } + + /** + * Removes the specified path if it exists. + * + * @param path the path to remove + * @return true if the path was removed, false if it did not existed + */ + boolean remove( String path ) + { + return pathsSet.remove( normalizeSubPath( path ) ); + } + + /** + * Returns iterator of normalized paths (strings) + * + * @return iterator of normalized paths (strings) + */ + @Override + public Iterator iterator() + { + return pathsSet.iterator(); + } + + /** + * @return {@link #pathsSet} + */ + public Collection paths() + { + return pathsSet; + } + + /** + * Adds given prefix to all paths in the set. + * + * The prefix should be ended by '/'. The generated paths are normalized. + * + * @param prefix to be added to all items + */ + public void addPrefix( String prefix ) + { + final Set newSet = new HashSet<>(); + for ( String path : pathsSet ) + { + newSet.add( normalizeSubPath( prefix + path ) ); + } + pathsSet = newSet; + } + + /** + * Returns count of the paths in the set + * + * @return count of the paths in the set + */ + public int size() + { + return pathsSet.size(); + } + + /** + * Adds to the set all files in the given directory + * + * @param directory that will be searched for file's paths to add + * @param prefix to be added to all found files + */ + public void addAllFilesInDirectory( File directory, String prefix ) + { + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( directory ); + scanner.scan(); + addAll( scanner.getIncludedFiles(), prefix ); + } + +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WarUtils.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WarUtils.java new file mode 100644 index 000000000..19dbb7980 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WarUtils.java @@ -0,0 +1,105 @@ +package org.apache.maven.plugins.war.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.Objects; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Dependency; +import org.apache.maven.project.MavenProject; + +/** + * @author Stephane Nicoll + */ +public class WarUtils +{ + + /** + * @param project {@link MavenProject} + * @param dependency {@link Dependency} + * @return {@link Artifact} + */ + public static Artifact getArtifact( MavenProject project, Dependency dependency ) + { + for ( Artifact artifact : project.getArtifacts() ) + { + if ( artifact.getGroupId().equals( dependency.getGroupId() ) + && artifact.getArtifactId().equals( dependency.getArtifactId() ) + && artifact.getType().equals( dependency.getType() ) ) + { + if ( artifact.getClassifier() == null && dependency.getClassifier() == null ) + { + return artifact; + } + else if ( dependency.getClassifier() != null + && dependency.getClassifier().equals( artifact.getClassifier() ) ) + { + return artifact; + } + } + } + return null; + } + + /** + * @param artifact {@link Artifact} + * @param dependency {@link Dependency} + * @return is related or not. + */ + public static boolean isRelated( Artifact artifact, Dependency dependency ) + { + if ( artifact == null || dependency == null ) + { + return false; + } + + if ( !Objects.equals( artifact.getGroupId(), dependency.getGroupId() ) ) + { + return false; + } + if ( !Objects.equals( artifact.getArtifactId(), dependency.getArtifactId() ) ) + { + return false; + } + if ( Objects.equals( artifact.getVersion(), dependency.getVersion() ) ) + { + return false; + } + if ( Objects.equals( artifact.getType(), dependency.getType() ) ) + { + return false; + } + if ( Objects.equals( artifact.getClassifier(), dependency.getClassifier() ) ) + { + return false; + } + if ( Objects.equals( artifact.getScope(), dependency.getScope() ) ) + { + return false; + } + if ( artifact.isOptional() != dependency.isOptional() ) + { + return false; + } + + return true; + } + +} diff --git a/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WebappStructure.java b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WebappStructure.java new file mode 100644 index 000000000..9d116508a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/java/org/apache/maven/plugins/war/util/WebappStructure.java @@ -0,0 +1,391 @@ +package org.apache.maven.plugins.war.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Dependency; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Represents the structure of a web application composed of multiple overlays. Each overlay is registered within this + * structure with the set of files it holds. + * + * Note that this structure is persisted to disk at each invocation to store which owner holds which path (file). + * + * @author Stephane Nicoll + */ +public class WebappStructure +{ + + private Map registeredFiles; + + private List dependenciesInfo; + + private transient PathSet allFiles = new PathSet(); + + /** + * Creates a new empty instance. + * + * @param dependencies the dependencies of the project + */ + public WebappStructure( List dependencies ) + { + this.dependenciesInfo = createDependenciesInfoList( dependencies ); + this.registeredFiles = new HashMap<>(); + } + + /** + * Returns the list of {@link DependencyInfo} for the project. + * + * @return the dependencies information of the project + */ + public List getDependenciesInfo() + { + return dependenciesInfo; + } + + /** + * Returns the dependencies of the project. + * + * @return the dependencies of the project + */ + public List getDependencies() + { + final List result = new ArrayList<>(); + if ( dependenciesInfo == null ) + { + return result; + } + for ( DependencyInfo dependencyInfo : dependenciesInfo ) + { + result.add( dependencyInfo.getDependency() ); + } + return result; + } + + /** + * Specify if the specified path is registered or not. + * + * @param path the relative path from the webapp root directory + * @return true if the path is registered, false otherwise + */ + public boolean isRegistered( String path ) + { + return getFullStructure().contains( path ); + + } + + /** + * Registers the specified path for the specified owner. Returns true if the path is not already + * registered, false otherwise. + * + * @param id the owner of the path + * @param path the relative path from the webapp root directory + * @return true if the file was registered successfully + */ + public boolean registerFile( String id, String path ) + { + if ( !isRegistered( path ) ) + { + doRegister( id, path ); + return true; + } + else + { + return false; + } + } + + /** + * Forces the registration of the specified path for the specified owner. If the file is not registered yet, a + * simple registration is performed. If the file already exists, the owner changes to the specified one. + *

+ * Beware that the semantic of the return boolean is different than the one from + * {@link #registerFile(String, String)}; returns true if an owner replacement was made and false + * if the file was simply registered for the first time.

+ * + * @param id the owner of the path + * @param path the relative path from the webapp root directory + * @return false if the file did not exist, true if the owner was replaced + */ + public boolean registerFileForced( String id, String path ) + { + if ( !isRegistered( path ) ) + { + doRegister( id, path ); + return false; + } + else + { + // Force the switch to the new owner + getStructure( getOwner( path ) ).remove( path ); + getStructure( id ).add( path ); + return true; + } + + } + + /** + * Registers the specified path for the specified owner. Invokes the callback with the result of the + * registration. + * + * @param id the owner of the path + * @param path the relative path from the webapp root directory + * @param callback the callback to invoke with the result of the registration + * @throws IOException if the callback invocation throws an IOException + */ + public void registerFile( String id, String path, RegistrationCallback callback ) + throws IOException + { + + // If the file is already in the current structure, rejects it with the current owner + if ( isRegistered( path ) ) + { + callback.refused( id, path, getOwner( path ) ); + } + else + { + doRegister( id, path ); + // This is a new file + if ( getOwner( path ) == null ) + { + callback.registered( id, path ); + + } // The file already belonged to this owner + else if ( getOwner( path ).equals( id ) ) + { + callback.alreadyRegistered( id, path ); + } // The file belongs to another owner and it's known currently + else if ( getOwners().contains( getOwner( path ) ) ) + { + callback.superseded( id, path, getOwner( path ) ); + } // The file belongs to another owner and it's unknown + else + { + callback.supersededUnknownOwner( id, path, getOwner( path ) ); + } + } + } + + /** + * Returns the owner of the specified path. If the file is not registered, returns null + * + * @param path the relative path from the webapp root directory + * @return the owner or null. + */ + public String getOwner( String path ) + { + if ( !isRegistered( path ) ) + { + return null; + } + else + { + for ( final String owner : registeredFiles.keySet() ) + { + final PathSet structure = getStructure( owner ); + if ( structure.contains( path ) ) + { + return owner; + } + + } + throw new IllegalStateException( "Should not happen, path [" + path + + "] is flagged as being registered but was not found." ); + } + + } + + /** + * Returns the owners. + * + * @return the list of owners + */ + public Set getOwners() + { + return registeredFiles.keySet(); + } + + /** + * Returns all paths that have been registered so far. + * + * @return all registered path + */ + public PathSet getFullStructure() + { + return allFiles; + } + + /** + * Returns the list of registered files for the specified owner. + * + * @param id the owner + * @return the list of files registered for that owner + */ + public PathSet getStructure( String id ) + { + PathSet pathSet = registeredFiles.get( id ); + if ( pathSet == null ) + { + pathSet = new PathSet(); + registeredFiles.put( id, pathSet ); + } + return pathSet; + } + + + + /** + * Registers the target file name for the specified artifact. + * + * @param artifact the artifact + * @param targetFileName the target file name + */ + public void registerTargetFileName( Artifact artifact, String targetFileName ) + { + if ( dependenciesInfo != null ) + { + for ( DependencyInfo dependencyInfo : dependenciesInfo ) + { + if ( WarUtils.isRelated( artifact, dependencyInfo.getDependency() ) ) + { + dependencyInfo.setTargetFileName( targetFileName ); + } + } + } + } + + // Private helpers + + private void doRegister( String id, String path ) + { + getFullStructure().add( path ); + getStructure( id ).add( path ); + } + + private List createDependenciesInfoList( List dependencies ) + { + if ( dependencies == null ) + { + return Collections.emptyList(); + } + final List result = new ArrayList<>(); + for ( Dependency dependency : dependencies ) + { + result.add( new DependencyInfo( dependency ) ); + } + return result; + } + + private Object readResolve() + { + // the full structure should be resolved so let's rebuild it + this.allFiles = new PathSet(); + for ( PathSet pathSet : registeredFiles.values() ) + { + this.allFiles.addAll( pathSet ); + } + return this; + } + + /** + * Callback interface to handle events related to filepath registration in the webapp. + */ + public interface RegistrationCallback + { + + /** + * Called if the targetFilename for the specified ownerId has been registered successfully. + * + * This means that the targetFilename was unknown and has been registered successfully. + * + * @param ownerId the ownerId + * @param targetFilename the relative path according to the root of the webapp + * @throws IOException if an error occurred while handling this event + */ + void registered( String ownerId, String targetFilename ) + throws IOException; + + /** + * Called if the targetFilename for the specified ownerId has already been registered. + * + * This means that the targetFilename was known and belongs to the specified owner. + * + * @param ownerId the ownerId + * @param targetFilename the relative path according to the root of the webapp + * @throws IOException if an error occurred while handling this event + */ + void alreadyRegistered( String ownerId, String targetFilename ) + throws IOException; + + /** + *

+ * Called if the registration of the targetFilename for the specified ownerId has been refused + * since the path already belongs to the actualOwnerId. + *

+ * This means that the targetFilename was known and does not belong to the specified owner. + * + * @param ownerId the ownerId + * @param targetFilename the relative path according to the root of the webapp + * @param actualOwnerId the actual owner + * @throws IOException if an error occurred while handling this event + */ + void refused( String ownerId, String targetFilename, String actualOwnerId ) + throws IOException; + + /** + * Called if the targetFilename for the specified ownerId has been registered successfully by + * superseding a deprecatedOwnerId, that is the previous owner of the file. + * + * This means that the targetFilename was known but for another owner. This usually happens after a + * project's configuration change. As a result, the file has been registered successfully to the new owner. + * + * @param ownerId the ownerId + * @param targetFilename the relative path according to the root of the webapp + * @param deprecatedOwnerId the previous owner that does not exist anymore + * @throws IOException if an error occurred while handling this event + */ + void superseded( String ownerId, String targetFilename, String deprecatedOwnerId ) + throws IOException; + + /** + * Called if the targetFilename for the specified ownerId has been registered successfully by + * superseding a unknownOwnerId, that is an owner that does not exist anymore in the current project. + * + * This means that the targetFilename was known but for an owner that does not exist anymore. Hence the + * file has been registered successfully to the new owner. + * + * @param ownerId the ownerId + * @param targetFilename the relative path according to the root of the webapp + * @param unknownOwnerId the previous owner that does not exist anymore + * @throws IOException if an error occurred while handling this event + */ + void supersededUnknownOwner( String ownerId, String targetFilename, String unknownOwnerId ) + throws IOException; + } + +} diff --git a/Java-base/maven-war-plugin/src/src/main/resources-filtered/META-INF/plexus/components.xml b/Java-base/maven-war-plugin/src/src/main/resources-filtered/META-INF/plexus/components.xml new file mode 100644 index 000000000..85adcb528 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/main/resources-filtered/META-INF/plexus/components.xml @@ -0,0 +1,89 @@ + + + + + + + + + org.apache.maven.artifact.handler.ArtifactHandler + war + org.apache.maven.artifact.handler.DefaultArtifactHandler + + war + true + java + false + + + + + + + org.apache.maven.lifecycle.mapping.LifecycleMapping + war + org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping + + + + default + + + + org.apache.maven.plugins:maven-resources-plugin:3.0.2:resources + + + org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile + + + org.apache.maven.plugins:maven-resources-plugin:3.0.2:testResources + + + org.apache.maven.plugins:maven-compiler-plugin:3.7.0:testCompile + + + org.apache.maven.plugins:maven-surefire-plugin:2.19.1:test + + + org.apache.maven.plugins:maven-war-plugin:${project.version}:war + + + org.apache.maven.plugins:maven-install-plugin:2.5.2:install + + + org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy + + + + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/site/apt/examples/adding-filtering-webresources.apt.vm b/Java-base/maven-war-plugin/src/src/site/apt/examples/adding-filtering-webresources.apt.vm new file mode 100644 index 000000000..56f97baeb --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/examples/adding-filtering-webresources.apt.vm @@ -0,0 +1,407 @@ + ------ + Adding and Filtering External Web Resources + ------ + Pete Marvin King + Dennis Lundberg + ------ + 2012-09-28 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Adding and Filtering External Web Resources + + The default resource directory for all Maven projects is <<>> which + will end up in <<>> and in <<>> in the WAR. The directory + structure will be preserved in the process. + + The WAR Plugin is also capable of including resources not found in the default resource + directory through the <<>> parameter. + +*Adding web resources + ++-------+ + + ... + + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + + + + resource2 + + + + + + + ... + ++-------+ + + Using our sample project in the {{{../usage.html}usage section}} with an added external resource, like this: + +---------- + . + |-- pom.xml + |-- resource2 + | |-- external-resource.jpg + | `-- image2 + | `-- external-resource2.jpg + `-- src + `-- main + |-- java + | `-- com + | `-- example + | `-- projects + | `-- SampleAction.java + |-- resources + | `-- images + | `-- sampleimage.jpg + `-- webapp + |-- WEB-INF + | `-- web.xml + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- + + would end up in the WAR as: + +---------- +documentedproject-1.0-SNAPSHOT.war + |-- META-INF + | |-- MANIFEST.MF + | `-- maven + | `-- com.example.projects + | `-- documentedproject + | |-- pom.properties + | `-- pom.xml + |-- WEB-INF + | |-- classes + | | |-- com + | | | `-- example + | | | `-- projects + | | | `-- SampleAction.class + | | `-- images + | | `-- sampleimage.jpg + | `-- web.xml + |-- external-resource.jpg + |-- image2 + | `-- external-resource2.jpg + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- + + <<>> and <<>> are copied to the root of the WAR, preserving the + directory structure. + +*Configuring web Resources + + <<>> is a list of resources. All options of resource are supported. + + A web resource + + * can have includes/excludes + + * can be filtered + + * is not limited to the default destination - the root of the WAR + +**Includes/Excludes + + To include all jpgs in the WAR we can add the following to our POM configuration from above: + ++----------+ + ... + + + + + resource2 + + + **/*.jpg + + + + + ... ++----------+ + + To exclude the <<>> directory from the WAR add this: + ++----------+ + ... + + + + + resource2 + + + **/image2 + + + + + ... ++----------+ + + Be careful when mixing includes and excludes, excludes will have a higher priority. + Includes can not override excludes if a resource matches both. + + Having this configuration will exclude all jpgs from the WAR: + ++----------+ + ... + + + + + resource2/ + + + image2/*.jpg + + + + **/*.jpg + + + + + ... ++----------+ + + Here's another example of how to specify include and exclude patterns: + ++----------+ + ... + + + + + resource2 + + + **/pattern1 + *pattern2 + + + + *pattern3/pattern3 + pattern4/pattern4 + + + + + ... ++----------+ + +**Filtering + + Using our example above, we can also configure filters for our resources. + We will add a hypothetical <<>> directory to our project: + +---------- + . + |-- configurations + | |-- config.cfg + | `-- properties + | `-- config.prop + |-- pom.xml + |-- resource2 + | |-- external-resource.jpg + | `-- image2 + | `-- external-resource2.jpg + `-- src + `-- main + |-- java + | `-- com + | `-- example + | `-- projects + | `-- SampleAction.java + |-- resources + | `-- images + | `-- sampleimage.jpg + `-- webapp + |-- WEB-INF + | `-- web.xml + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- + + To prevent corrupting your binary files when filtering is enabled, you can configure a list of file extensions that + will not be filtered. + ++----------+ + ... + + + + + properties/config.prop + + + + pdf + + + + resource2 + + false + + + configurations + + true + + **/properties + + + + + ... ++----------+ + +*** <<>> + ++----------+ +interpolated_property=some_config_value ++----------+ + +*** <<>> + ++----------+ + + ${interpolated_property} + ++----------+ + + The resulting WAR would be: + +---------- +documentedproject-1.0-SNAPSHOT.war + |-- META-INF + | |-- MANIFEST.MF + | `-- maven + | `-- com.example.projects + | `-- documentedproject + | |-- pom.properties + | `-- pom.xml + |-- WEB-INF + | |-- classes + | | |-- com + | | | `-- example + | | | `-- projects + | | | `-- SampleAction.class + | | `-- images + | | `-- sampleimage.jpg + | `-- web.xml + |-- config.cfg + |-- external-resource.jpg + |-- image2 + | `-- external-resource2.jpg + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- + + and the content of <<>> would be: + ++----------+ + + some_config_value + ++----------+ + + <> In versions 2.2 and earlier of this plugin the platform encoding was + used when filtering resources. Depending on what that encoding was you could + end up with scrambled characters after filtering. Starting with version 2.3 + this plugin respects the property <<>> when + filtering resources. One notable exception to this is that <<<.xml>>> files + are filtered using the encoding specified inside the xml-file itself. + + +**Overriding the default destination directory + + By default web resources are copied to the root of the WAR, as shown in the previous example. + To override the default destination directory, specify the target path. + ++----------+ + ... + + + + ... + + + configurations + + WEB-INF + + true + + **/properties + + + + + ... ++----------+ + + Using the sample project the resulting WAR would look like this: + +---------- +documentedproject-1.0-SNAPSHOT.war + |-- META-INF + | |-- MANIFEST.MF + | `-- maven + | `-- com.example.projects + | `-- documentedproject + | |-- pom.properties + | `-- pom.xml + |-- WEB-INF + | |-- classes + | | |-- com + | | | `-- example + | | | `-- projects + | | | `-- SampleAction.class + | | `-- images + | | `-- sampleimage.jpg + | |-- config.cfg + | `-- web.xml + |-- external-resource.jpg + |-- image2 + | `-- external-resource2.jpg + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- diff --git a/Java-base/maven-war-plugin/src/src/site/apt/examples/file-name-mapping.apt b/Java-base/maven-war-plugin/src/src/site/apt/examples/file-name-mapping.apt new file mode 100644 index 000000000..0e96b41b2 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/examples/file-name-mapping.apt @@ -0,0 +1,60 @@ + ------ + Using File Name Mapping + ------ + Stephane Nicoll + Dennis Lundberg + ------ + 2010-05-24 + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Using File Name Mapping + + It might be necessary to customize the file name of libraries and TLDs. By default, those resources are stored + using the following pattern: + ++----- +@{artifactId}@-@{version}@.@{extension}@ ++----- + + If the artifact has a classifier the default pattern is of course: + ++----- +@{artifactId}@-@{version}@-@{classifier}@.@{extension}@ ++----- + + The <<>> parameter allows you to give a custom pattern. Each token defined in the + pattern will be replaced with a value from the current artifact. You can use any property of Artifact and + ArtifactHandler as a token. There is also a special token named <<>> that can be used, since 2.1. + It will add the string "-yourclassifier" if and only if the artifact has a classifier. + + For instance, to store the libraries and TLDs without version numbers or classifiers, use the following pattern: + ++----- +@{artifactId}@.@{extension}@ ++----- + + To store the libraries and TLDs without version numbers but with classifiers + (if they exist), use the following pattern: + ++----- +@{artifactId}@@{dashClassifier?}@.@{extension}@ ++----- diff --git a/Java-base/maven-war-plugin/src/src/site/apt/examples/including-excluding-files-from-war.apt.vm b/Java-base/maven-war-plugin/src/src/site/apt/examples/including-excluding-files-from-war.apt.vm new file mode 100644 index 000000000..74a4e71da --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/examples/including-excluding-files-from-war.apt.vm @@ -0,0 +1,94 @@ + ------ + Including and Excluding Files From the WAR + ------ + Dennis Lundberg + ------ + 2011-11-21 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Including and Excluding Files From the WAR + + + It is possible to include or exclude certain files from the WAR file, by using + the <<<\>>> and <<<\>>> configuration + parameters. They each take a comma-separated list of Ant file set patterns. + You can use wildcards such as <<<**>>> to indicate multiple directories and + <<<*>>> to indicate an optional part of a file or directory name. + + Here is an example where we exclude all JAR files from <<>>: + ++-----------------+ + + ... + + + + maven-war-plugin + ${project.version} + + WEB-INF/lib/*.jar + + + + + ... + ++-----------------+ + + Sometimes even such wildcards are not enough. In these cases you can use + regular expressions with the <<<%regex[]>>> syntax. Here is a real life use + case in which this is used. In this example we want to exclude any + commons-logging and log4j JARs, but we do not want to exclude the + log4j-over-slf4j JAR. So we want to exclude <<.jar>>> but + keep the <<.jar>>>. + ++-----------------+ + + ... + + + + maven-war-plugin + ${project.version} + + + + WEB-INF/lib/commons-logging-*.jar, + %regex[WEB-INF/lib/log4j-(?!over-slf4j).*.jar] + + + + + + ... + ++-----------------+ + + If you have more real life examples of using regular expressions, we'd like to + know about them. Please file an issue in + {{{../issue-tracking.html}our issue tracker}} with your configuration, so we + can expand this page. diff --git a/Java-base/maven-war-plugin/src/src/site/apt/examples/rapid-testing-jetty6-plugin.apt b/Java-base/maven-war-plugin/src/src/site/apt/examples/rapid-testing-jetty6-plugin.apt new file mode 100644 index 000000000..a500a933b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/examples/rapid-testing-jetty6-plugin.apt @@ -0,0 +1,75 @@ + ------ + Rapid Testing Using the Jetty Plugin + ------ + Pete Marvin King + ------ + 2008-08-03 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Rapid Testing Using the Jetty Plugin + + Normally, testing a web application involves compiling Java sources, creating a WAR and + deploying it to a web container. + + Using the Jetty Plugin enables you to quickly test your web application by skipping + the last two steps. By default the Jetty Plugin scans <<>> for + any changes in your Java sources and <<>> for changes to your web sources. + The Jetty Plugin will automatically reload the modified classes and web sources. + + To use the Jetty Plugin just add the following in your <<>>: + ++-----------------+ + + ... + + + + org.mortbay.jetty + maven-jetty-plugin + 6.1.10 + + 10 + + + 8080 + 60000 + + + + + ... + + + ... + ++-----------------+ + + Then start Jetty: + +----------------- + mvn jetty:run +----------------- + + The command will block with Jetty listening on port 8080. + + Check the {{{http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin}Jetty Plugin documentation}} for more details. diff --git a/Java-base/maven-war-plugin/src/src/site/apt/examples/skinny-wars.apt.vm b/Java-base/maven-war-plugin/src/src/site/apt/examples/skinny-wars.apt.vm new file mode 100644 index 000000000..f530dbdde --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/examples/skinny-wars.apt.vm @@ -0,0 +1,112 @@ + ------ + Creating Skinny WARs + ------ + Mike Perham + Karl-Heinz Marbaise + ------ + 2014-10-01 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Creating Skinny WARs + + In a typical J2EE environment, a WAR is packaged within an EAR for deployment. The + WAR can contain all its dependent JARs in <<>> but then the EAR can quickly grow + very large if there are multiple WARs, due to the presence of duplicate JARs. Instead + the J2EE specification allows WARs to reference external JARs packaged within the EAR + via the <<>> setting in their <<>>. + + The Maven EAR Plugin has direct support for creating skinny wars which simply + means to {{{http://maven.apache.org/plugins/maven-ear-plugin/examples/skinny-wars.html}configure the maven-ear-plugin}} + accordingly. + + You need to change the EAR project's <<>> to package those dependent JARs in the EAR. + Notice that we package everything into a <<>> directory within the EAR. This is + just my own personal preference to distinguish between J2EE modules (which will + be packaged in the root of the EAR) and Java libraries (which are packaged in <<>>). + ++-----------------+ + + ... + + + + maven-ear-plugin + 2.9.1 + + lib/ + true + + + + + ... + ++-----------------+ + + Now the painful part. Your EAR project's <<>> needs to list every dependency that the WAR has. + This is because Maven assumes fat WARs and does not include transitive dependencies + of WARs within the EAR. + ++-----------------+ + + .... + + + com.acme + shared-jar + 1.0.0 + + + com.acme + war1 + 1.0.0 + war + + + com.acme + war2 + 1.0.0 + war + + + ... + ++-----------------+ + + Your EAR will contain something like this: + +----------------- + . + |-- META-INF + | `-- application.xml + |-- lib + | `-- shared-jar-1.0.0.jar + |-- war1-1.0.0.war + `-- war2-1.0.0.war +----------------- + + +* Alternatives + + Our users have submitted alternatives to the above recipe on + {{{http://docs.codehaus.org/display/MAVENUSER/Solving+the+Skinny+Wars+problem}the Wiki}}. diff --git a/Java-base/maven-war-plugin/src/src/site/apt/examples/war-manifest-guide.apt.vm b/Java-base/maven-war-plugin/src/src/site/apt/examples/war-manifest-guide.apt.vm new file mode 100644 index 000000000..a3aadb302 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/examples/war-manifest-guide.apt.vm @@ -0,0 +1,102 @@ + ------ + WAR Manifest Customization + ------ + Pete Marvin King + ------ + 2008-08-03 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +WAR Manifest Customization + + The manifest can be customized by configuring the WAR Plugin's archiver. For full information on the different + configuration options available check the documentation for + {{{http://maven.apache.org/shared/maven-archiver/index.html}Maven Archiver}}. + + +*Generating a manifest classpath + + Generating a manifest classpath for a WAR is similar to for a JAR, but there are a couple of slight differences since + you normally don't want a JAR in both the manifest classpath and the <<>> directory. Customize the WAR + Plugin's archiver: + ++--------------------+ + + ... + + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + + + true + + + + + ... + + + ... + ++--------------------+ + + Now, you can control which dependencies are included in <<>> and in the manifest classpath by following + these examples. Maven will follow the transitive dependency tree until it gets to artifacts scoped as "provided". + + <> No way is shown how to include a dependency in <<>> but not in the manifest classpath. + ++--------------------+ + + ... + + + org.foo + bar-jar1 + ${pom.version} + true + + + + org.foo + bar-jar2 + ${pom.version} + + + + org.foo + bar-jar3 + ${pom.version} + provided + + + ... + + ... + ++--------------------+ + + Check the {{{http://maven.apache.org/guides/mini/guide-manifest.html}Guide to Working with Manifests}} for more + examples. diff --git a/Java-base/maven-war-plugin/src/src/site/apt/index.apt.vm b/Java-base/maven-war-plugin/src/src/site/apt/index.apt.vm new file mode 100644 index 000000000..edcc5c5c5 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/index.apt.vm @@ -0,0 +1,98 @@ + ------ + Introduction + ------ + Pete Marvin King + ------ + 2013-07-22 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +${project.name} + + The WAR Plugin is responsible for collecting all artifact dependencies, classes + and resources of the web application and packaging them into a web application + archive. + +* Goals Overview + + * {{{./war-mojo.html}war:war}} is the default goal invoked during the + <<>> phase for projects with a packaging type of <<>>. + It builds a WAR file. + + * {{{./exploded-mojo.html}war:exploded}} is generally used to speed up + testing during the developement phase by creating an exploded webapp + in a specified directory. + + * {{{./inplace-mojo.html}war:inplace}} another variation of <<>> + where the webapp is instead generated in the web application source directory, + which is <<>> by default. + + [] + +* Usage + + General instructions on how to use the WAR Plugin can be found on the + {{{./usage.html}usage page}}. Some more specific use cases are described in the + examples given below. To share common resources across multiple web applications, + see the documentation about using {{{./overlays.html}overlays}}. + + In case you still have questions regarding the plugin's usage, please have a look at the + {{{./faq.html}FAQ}} and feel free to contact the {{{./mailing-lists.html}user mailing list}}. + The posts to the mailing list are archived and could already contain the answer to your + question as part of an older thread. Hence, it is also worth browsing/searching + the {{{./mailing-lists.html}mail archive}}. + + If you feel like the plugin is missing a feature or has a defect, you can fill a + feature request or bug report in our {{{./issue-management.html}issue tracker}}. + When creating a new issue, please provide a comprehensive description of your + concern. Especially for fixing bugs it is crucial that the developers can + reproduce your problem. For this reason, entire debug logs, POMs or most + preferably little demo projects attached to the issue are very much appreciated. + Of course, patches are welcome, too. Contributors can check out the project from + our {{{./scm.html}source repository}} and will find supplementary + information in the + {{{/guides/development/guide-helping.html}guide to helping with Maven}}. + +* Examples + + To provide you with better understanding on some usages of the Maven WAR Plugin, + you can take a look into the following examples: + + * {{{./examples/adding-filtering-webresources.html}Adding and Filtering External Web Resources}} + + * {{{./examples/war-manifest-guide.html}WAR Manifest Customization}} + + * {{{./examples/rapid-testing-jetty6-plugin.html}Rapid Testing the Jetty Plugin}} + + * {{{./examples/skinny-wars.html}Creating Skinny WARs}} + + * {{{./examples/including-excluding-files-from-war.html}Including and Excluding Files From the WAR}} + + * {{{./examples/file-name-mapping.html}Using File Name Mapping}} + + [] + +* Related links + + * {{{/guides/mini/guide-archive-configuration.html}Exclusion of Maven Descriptors}} + + [] diff --git a/Java-base/maven-war-plugin/src/src/site/apt/overlays.apt.vm b/Java-base/maven-war-plugin/src/src/site/apt/overlays.apt.vm new file mode 100644 index 000000000..741a80e59 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/overlays.apt.vm @@ -0,0 +1,393 @@ + ------ + Overlays + ------ + Pete Marvin King + Stephane Nicoll + Dennis Lundberg + ------ + 2010-08-14 + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Overlays + + Overlays are used to share common resources across multiple web applications. + The dependencies of a WAR project are collected in <<>>, except for WAR + artifacts which are overlayed on the WAR project itself. + + +* Overlays at a glance + + To demonstrate, given this structure for the project <<>>: + +----------------- + |-- pom.xml + `-- src + `-- main + |-- java + | `-- com + | `-- example + | `-- projects + | `-- SampleAction.java + |-- resources + | |-- images + | | `-- sampleimage.jpg + | `-- sampleresource + `-- webapp + |-- WEB-INF + | `-- web.xml + |-- index.jsp + `-- jsp + `-- websource.jsp +----------------- + + The project depends on another WAR artifact, <<>>, + which is declared as a dependency in the project's <<>>: + ++-----------------+ + + ... + + + com.example.projects + documentedprojectdependency + 1.0-SNAPSHOT + war + runtime + + ... + + ... + ++-----------------+ + + The structure for the <<>> WAR file looks like this: + +----------------- +documentedprojectdependency-1.0-SNAPSHOT.war + |-- META-INF + | |-- MANIFEST.MF + | `-- maven + | `-- com.example.projects + | `-- documentedprojectdependency + | |-- pom.properties + | `-- pom.xml + |-- WEB-INF + | |-- classes + | | |-- com + | | | `-- example + | | | `-- projects + | | | `-- SampleActionDependency.class + | | `-- images + | | `-- sampleimage-dependency.jpg + | `-- web.xml + `-- index-dependency.jsp +----------------- + + The resulting WAR would end up like this: + +----------------- + |-- META-INF + | |-- MANIFEST.MF + | `-- maven + | `-- com.example.projects + | `-- documentedproject + | |-- pom.properties + | `-- pom.xml + |-- WEB-INF + | |-- classes + | | |-- com + | | | `-- example + | | | `-- projects + | | | |-- SampleAction.class + | | | `-- SampleActionDependency.class + | | `-- images + | | |-- sampleimage-dependency.jpg + | | `-- sampleimage.jpg + | `-- web.xml + |-- index-dependency.jsp + |-- index.jsp + `-- jsp + `-- websource.jsp +----------------- + + The <<>> file above comes from <<>>. + + +* Overlay types + + The WAR Plugin handles both <> and <> artifacts as overlays. However, for backward compatibility reasons, zip + overlays are handled only if they are defined explicitly in the plugin's configuration. + + +* Configuring Overlays + + In previous versions of the WAR Plugin, no configuration was necessary. This is still the + case if you are happy with the default settings. However, if you need more control, read on! + + The <<<\<{overlay}\>>>> element can have the following child elements: + + * <> - the id of the overlay. If none is provided, the WAR Plugin will + generate one. + + * <> - the groupId of the overlay artifact you want to configure. + + * <> - the artifactId of the overlay artifact you want to + configure. + + * <> - the type of the overlay artifact you want to + configure. Default value is: <<>>. + + * <> - the classifier of the overlay artifact you want to + configure if multiple artifacts matches the groupId/artifactId. + + * <> - the files to include. By default, all files are included. + + * <> - the files to exclude. By default, the <<>> file + is excluded. + + * <> - the target relative path in the webapp structure, which is only + available for overlays of type <<>>. By default, the content of the overlay is added in the root + structure of the webapp. + + * <> - set to <<>> to skip this overlay. Default value is: <<>>. + + * <> - whether to apply filtering to this overlay. Default value is <<>>. + + [] + + For instance, to exclude the <<>> of our + <<>> <<>> overlay above: + ++-----------------+ + + ... + + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + + + com.example.projects + documentedprojectdependency + + WEB-INF/classes/images/sampleimage-dependency.jpg + + + + + + + + ... + ++-----------------+ + + +* Overlays packaging + + Overlays are applied with a first-win strategy (hence if a file has been copied by + one overlay, it won't be copied by another). Overlays are applied in the order in which + they are defined in the <<<\>>> configuration. If no configuration is provided, + the order in which the dependencies are defined in the POM is used (warning: this is not + deterministic, especially if you have overlays as transitive dependencies). In case of + a mixed situation (e.g. configured overlays and non-configured overlays), non-configured + overlays are applied after configured overlays. + + By default, the source of the project (a.k.a the current build) is added first (e.g. + before any overlay is applied). The current build is defined as a special overlay + with no <<>>, <<>>. If overlays need to be applied first, simply + configure the current build after those overlays. + + For instance, if <<>> from the <<>> group is a dependency + of the project and needs to be applied before the project's own source, do as follows: + + ++-----------------+ + + ... + + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + + + com.example.projects + my-webapp + + + + + + + + + + ... + ++-----------------+ + + <> In the scenario above, any other WAR dependency will be applied after the current build + since they have not been configured in the <<<\>>> element. + + To perform an even more fine grained overwriting policy, overlays can be packaged multiple times + with different includes/excludes. For instance if the <<>> file of the + overlay <<>> <> be set in the webapp but other files can be controlled the + regular way, define two overlay configurations for <<>>: + ++-----------------+ + + ... + + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + + + my-webapp-index.jsp + com.example.projects + my-webapp + + index.jsp + + + + + + + + + + my-webapp + com.example.projects + my-webapp + + + + + + + ... + ++-----------------+ + + +* Overlay global settings + + The following settings can be specified globally and modify the way all overlays are applied. + + * <> - sets the default includes to apply to all overlays. Any overlay that + has no specific <<>> element will inherit this setting by default. + ++-----------------+ + + ... + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + **/IncludeME,**/images + + + + ... + ++-----------------+ + + * <> - sets the default excludes to apply to all overlays. Any overlay that + has no specific <<>> element will inherit this setting by default. + ++-----------------+ + + ... + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + WEB-INF/web.xml,index.* + + + + ... + ++-----------------+ + + * <> - sets the directory where overlays will be temporarily extracted. + ++-----------------+ + + ... + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + + /tmp/extract_here + + + + ... + ++-----------------+ + +* Zip dependencies with overlays + + To use a <> dependency as an overlay you have to configure it explicitly in the plugin's configuration. For + instance to inject the content of a zip overlay in the <<>> directory of the webapp, do as follows: + ++-----------------+ + + ... + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + + + zipGroupId + zipArtifactId + zip + scripts + + + + + + ... + ++-----------------+ diff --git a/Java-base/maven-war-plugin/src/src/site/apt/usage.apt.vm b/Java-base/maven-war-plugin/src/src/site/apt/usage.apt.vm new file mode 100644 index 000000000..3e3b1cc0d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/apt/usage.apt.vm @@ -0,0 +1,229 @@ + ------ + Usage + ------ + Pete Marvin King + ------ + 2010-08-15 + ------ + +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you under the Apache License, Version 2.0 (the +~~ "License"); you may not use this file except in compliance +~~ with the License. You may obtain a copy of the License at +~~ +~~ http://www.apache.org/licenses/LICENSE-2.0 +~~ +~~ Unless required by applicable law or agreed to in writing, +~~ software distributed under the License is distributed on an +~~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +~~ KIND, either express or implied. See the License for the +~~ specific language governing permissions and limitations +~~ under the License. + +~~ NOTE: For help with the syntax of this file, see: +~~ http://maven.apache.org/doxia/references/apt-format.html + +Usage + + There are 4 ways to use the WAR Plugin: + + * using the <<>> phase with the project package type as <<>> + + * invocation of the <<>> goal + + * invocation of the <<>> goal + + * invocation of the <<>> goal + + [] + + <> When using the <<>> goals it is assumed that the <<>> phase is already done. + The WAR Plugin is not responsible for compiling the java sources or copying the resources. + + To handle archiving this version of Maven WAR Plugin uses + {{{http://maven.apache.org/shared/maven-archiver/index.html}Maven Archiver}} ${mavenArchiverVersion}. + + To handle filtering this version of Maven WAR Plugin uses + {{{http://maven.apache.org/shared/maven-filtering/index.html}Maven Filtering}} ${mavenFilteringVersion}. + + +*Using the <<>> phase with the project package type as war / invocation of the <<>> goal + + This is the normal way of using the WAR Plugin. + To illustrate, here's the <<>> for our project: + ++----------+ + + ... + com.example.projects + documentedproject + war + 1.0-SNAPSHOT + Documented Project + http://example.com + ... + ++----------+ + + The project's structure looks like this: + +---------- + |-- pom.xml + `-- src + `-- main + |-- java + | `-- com + | `-- example + | `-- projects + | `-- SampleAction.java + |-- resources + | `-- images + | `-- sampleimage.jpg + `-- webapp + |-- WEB-INF + | `-- web.xml + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- + + Invoking + +---------- +mvn package +---------- + + or + +---------- +mvn compile war:war +---------- + + will generate the WAR file <<>>. Here are the contents of that WAR file: + +---------- +documentedproject-1.0-SNAPSHOT.war + |-- META-INF + | |-- MANIFEST.MF + | `-- maven + | `-- com.example.projects + | `-- documentedproject + | |-- pom.properties + | `-- pom.xml + |-- WEB-INF + | |-- classes + | | |-- com + | | | `-- example + | | | `-- projects + | | | `-- SampleAction.class + | | `-- images + | | `-- sampleimage.jpg + | `-- web.xml + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- + +*Invocation of <<>> goal + + To speed up testing during the developement phase, <<>> can be used to generate the WAR in exploded + form. + Use the same project as above and invoke: + +---------- +mvn compile war:exploded +---------- + + This will generate an exploded version of the WAR in <<>>. + The contents of that directory looks like this: + +---------- + documentedproject-1.0-SNAPSHOT + |-- META-INF + |-- WEB-INF + | |-- classes + | | |-- com + | | | `-- example + | | | `-- projects + | | | `-- SampleAction.class + | | `-- images + | | `-- sampleimage.jpg + | `-- web.xml + |-- index.jsp + `-- jsp + `-- websource.jsp +---------- + + The default directory for the exploded WAR is <<>>>. The <<>> is usually in the form + of <<<\-\>>>. + This default directory can be overridden by specifying the <<>> parameter. + ++-------+ + + ... + + + + org.apache.maven.plugins + maven-war-plugin + ${project.version} + + /sample/servlet/container/deploy/directory + + + + + ... + ++-------+ + +*Invocation of <<>> goal + + Another variation of <<>> is <<>>. With <<>> the exploded WAR is created + in the webapp source, which defaults to <<>>. + Use our sample project above, and invoke: + +---------- +mvn compile war:inplace +---------- + + This will result in: + +---------- + |-- pom.xml + |-- src + | `-- main + | |-- java + | | `-- com + | | `-- example + | | `-- projects + | | `-- SampleAction.java + | |-- resources + | | `-- images + | | `-- sampleimage.jpg + | `-- webapp + | |-- META-INF + | |-- WEB-INF + | | |-- classes + | | | |-- com + | | | | `-- example + | | | | `-- projects + | | | | `-- SampleAction.class + | | | `-- images + | | | `-- sampleimage.jpg + | | `-- web.xml + | |-- index.jsp + | `-- jsp + | `-- websource.jsp + `-- target + `-- classes + |-- com + | `-- example + | `-- projects + | `-- SampleAction.class + `-- images + `-- sampleimage.jpg +---------- diff --git a/Java-base/maven-war-plugin/src/src/site/fml/faq.fml.vm b/Java-base/maven-war-plugin/src/src/site/fml/faq.fml.vm new file mode 100644 index 000000000..19fc6f81c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/fml/faq.fml.vm @@ -0,0 +1,141 @@ + + + + + + + + + How is filtering done in the WAR plugin? + +

To enable filtering of web.xml you must configure the WAR Plugin like this: + + maven-war-plugin + ${project.version} + + true + + +]]> +

+

Examples can be found here.

+
+
+ + How does the classifier of dependency artifacts affect my WAR project? + +

When used, the copy of the dependency artifact in your project will have the classifier appended to its + filename. This can be used to differentiate duplicate artifacts.

+
+
+ + How do I exclude transitive dependencies from my project? + +

Give it a "provided" scope.

+
+
+ + What's the difference between using dependentWarExclude and provided scope? + +

dependentWarExclude is used in overlays for excluding dependent + WAR files from the assembled webapp.

+
+
+ + How do I exclude files in my web resources? + +

Use the <webResources> / <exclude> parameter to identify the tokens to exclude.

+

For more information refer to Adding and Filtering + External Web Resources.

+
+
+ + How do I exclude files when doing overlays? + +

Use the dependentWarExcludes parameter to identify the tokens to exclude.

+
+
+ + Where can I find the documentation for the plugin's configuration? + +

+ For each goal, you can use the documentation from the plugin site. +

+

+ If you need a specific version, generate it using mvn site:site or use: + +]]> +

+
+
+ + How do I create a JAR containing the classes in my webapp? + +

If you would simply like to package the classes and resources as a JAR in WEB-INF/lib + rather than as loose files under WEB-INF/classes, use the following configuration:

+

+ + maven-war-plugin + ${project.version} + + true + + +]]> +

+

If you need to re-use this JAR in another project, the recommended approach is to move the classes to a + separate module that builds a JAR, and then declare a dependency on that JAR from your webapp as well as from + any other projects that need it.

+

If you can't move the classes to another project, you can deploy the classes and resources included in + your webapp as an "attached" artifact, with a classifier, by using the following configuration:

+

+ + ... + mywebapp + 1.0-SNAPSHOT + ... + + + + maven-war-plugin + ${project.version} + + true + + + + + ... + +]]> +

+

This will result in two artifacts being deployed: mywebapp-1.0-SNAPSHOT.war + and mywebapp-1.0-SNAPSHOT-classes.jar.

+
+
+
+
\ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/site/resources/download.cgi b/Java-base/maven-war-plugin/src/src/site/resources/download.cgi new file mode 100644 index 000000000..1b178d2e6 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/resources/download.cgi @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Just call the standard mirrors.cgi script. It will use download.html +# as the input template. +exec /www/www.apache.org/dyn/mirrors/mirrors.cgi $* \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/site/site.xml b/Java-base/maven-war-plugin/src/src/site/site.xml new file mode 100644 index 000000000..2f81f26ca --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/site.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/site/xdoc/download.xml.vm b/Java-base/maven-war-plugin/src/src/site/xdoc/download.xml.vm new file mode 100644 index 000000000..3f710359a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/site/xdoc/download.xml.vm @@ -0,0 +1,126 @@ + + + + + + + Download ${project.name} Source + + +
+ +

${project.name} ${project.version} is distributed in source format. Use a source archive if you intend to build + ${project.name} yourself. Otherwise, simply use the ready-made binary artifacts from central repository.

+ +

You will be prompted for a mirror - if the file is not found on yours, please be patient, as it may take 24 + hours to reach all mirrors.

+ +

In order to guard against corrupted downloads/installations, it is highly recommended to + verify the signature + of the release bundles against the public KEYS used by the Apache Maven + developers.

+ +

${project.name} is distributed under the Apache License, version 2.0.

+ +

We strongly encourage our users to configure a Maven repository mirror closer to their location, please read How to Use Mirrors for Repositories.

+ + + + +

+ [if-any logo] + + logo + + [end] + The currently selected mirror is + [preferred]. + If you encounter a problem with this mirror, + please select another mirror. + If all mirrors are failing, there are + backup + mirrors + (at the end of the mirrors list) that should be available. +

+ +
+ Other mirrors: + + +
+ +

+ You may also consult the + complete list of + mirrors. +

+ + + + + +

This is the current stable version of ${project.name}.

+ + + + + + + + + + + + + + + + + + +
LinkChecksumSignature
${project.name} ${project.version} (Source zip)maven/plugins/${project.artifactId}-${project.version}-source-release.zipmaven/plugins/${project.artifactId}-${project.version}-source-release.zip.sha512maven/plugins/${project.artifactId}-${project.version}-source-release.zip.asc
+
+ + + +

Older non-recommended releases can be found on our archive site.

+ +
+
+ +
+ diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java new file mode 100644 index 000000000..d7c897aa0 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java @@ -0,0 +1,303 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Stephane Nicoll + */ +public abstract class AbstractWarExplodedMojoTest + extends AbstractWarMojoTest +{ + + protected WarExplodedMojo mojo; + + public void setUp() + throws Exception + { + super.setUp(); + mojo = (WarExplodedMojo) lookupMojo( "exploded", getPomFile() ); + } + + /** + * Returns the pom configuration to use. + * + * @return the pom configuration + */ + protected abstract File getPomFile(); + + /** + * Returns the test directory to use. + * + * @return the test directory + */ + protected abstract File getTestDirectory(); + + /** + * Configures the exploded mojo for the specified test. + * + * If the sourceFiles parameter is null, sample JSPs are created by default. + * + * @param testId the id of the test + * @param artifactStubs the dependencies (may be null) + * @param sourceFiles the source files to create (may be null) + * @return the webapp directory + * @throws Exception if an error occurs while configuring the mojo + */ + protected File setUpMojo( final String testId, ArtifactStub[] artifactStubs, String[] sourceFiles ) + throws Exception + { + final MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + final File webAppDirectory = new File( getTestDirectory(), testId ); + + // Create the webapp sources + File webAppSource; + if ( sourceFiles == null ) + { + webAppSource = createWebAppSource( testId ); + } + else + { + webAppSource = createWebAppSource( testId, false ); + for ( String sourceFile : sourceFiles ) + { + File sample = new File( webAppSource, sourceFile ); + createFile( sample ); + + } + + } + + final File classesDir = createClassesDir( testId, true ); + final File workDirectory = new File( getTestDirectory(), "/war/work-" + testId ); + createDir( workDirectory ); + + if ( artifactStubs != null ) + { + for ( ArtifactStub artifactStub : artifactStubs ) + { + project.addArtifact( artifactStub ); + } + } + + configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "workDirectory", workDirectory ); + + return webAppDirectory; + } + + /** + * Configures the exploded mojo for the specified test. + * + * @param testId the id of the test + * @param artifactStubs the dependencies (may be null) + * @return the webapp directory + * @throws Exception if an error occurs while configuring the mojo + */ + protected File setUpMojo( final String testId, ArtifactStub[] artifactStubs ) + throws Exception + { + return setUpMojo( testId, artifactStubs, null ); + } + + /** + * Cleans up a directory. + * + * @param directory the directory to remove + * @throws IOException if an error occurred while removing the directory + */ + protected void cleanDirectory( File directory ) + throws IOException + { + if ( directory != null && directory.isDirectory() && directory.exists() ) + { + FileUtils.deleteDirectory( directory ); + } + } + + /** + * Asserts the default content of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @return a list of File objects that have been asserted + */ + protected List assertDefaultContent( File webAppDirectory ) + { + // Validate content of the webapp + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + + assertTrue( "source file not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source file not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + + final List content = new ArrayList<>(); + content.add( expectedWebSourceFile ); + content.add( expectedWebSource2File ); + + return content; + } + + /** + * Asserts the web.xml file of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @return a list with the web.xml File object + */ + protected List assertWebXml( File webAppDirectory ) + { + File expectedWEBXMLFile = new File( webAppDirectory, "WEB-INF/web.xml" ); + assertTrue( "web xml not found: " + expectedWEBXMLFile.toString(), expectedWEBXMLFile.exists() ); + + final List content = new ArrayList<>(); + content.add( expectedWEBXMLFile ); + + return content; + } + + /** + * Asserts custom content of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @param filePaths an array of file paths relative to the webapp directory + * @param customMessage a custom message if an assertion fails + * @return a list of File objects that have been inspected + */ + protected List assertCustomContent( File webAppDirectory, String[] filePaths, String customMessage ) + { + final List content = new ArrayList<>(); + for ( String filePath : filePaths ) + { + final File expectedFile = new File( webAppDirectory, filePath ); + if ( customMessage != null ) + { + assertTrue( customMessage + " - " + expectedFile.toString(), expectedFile.exists() ); + } + else + { + assertTrue( "source file not found: " + expectedFile.toString(), expectedFile.exists() ); + } + content.add( expectedFile ); + } + return content; + } + + /** + * Asserts that the webapp contains only the specified files. + * + * @param webAppDirectory the webapp directory + * @param expectedFiles the expected files + * @param filter an optional filter to ignore some resources + */ + protected void assertWebAppContent( File webAppDirectory, List expectedFiles, FileFilter filter ) + { + final List webAppContent = new ArrayList<>(); + if ( filter != null ) + { + buildFilesList( webAppDirectory, filter, webAppContent ); + } + else + { + buildFilesList( webAppDirectory, new FileFilterImpl( webAppDirectory, null ), webAppContent ); + } + + // Now we have the files, sort them. + Collections.sort( expectedFiles ); + Collections.sort( webAppContent ); + assertEquals( "Invalid webapp content, expected " + expectedFiles.size() + "file(s) " + expectedFiles + + " but got " + webAppContent.size() + " file(s) " + webAppContent, expectedFiles, webAppContent ); + } + + /** + * Builds the list of files and directories from the specified dir. + * + * Note that the filter is not used the usual way. If the filter does not accept the current file, it's not added + * but yet the subdirectories are added if any. + * + * @param dir the base directory + * @param filter the filter + * @param content the current content, updated recursively + */ + private void buildFilesList( final File dir, FileFilter filter, final List content ) + { + final File[] files = dir.listFiles(); + + for ( File file : files ) + { + // Add the file if the filter is ok with it + if ( filter.accept( file ) ) + { + content.add( file ); + } + + // Even if the file is not accepted and is a directory, add it + if ( file.isDirectory() ) + { + buildFilesList( file, filter, content ); + } + + } + } + + class FileFilterImpl + implements FileFilter + { + + private final List rejectedFilePaths; + + private final int webAppDirIndex; + + public FileFilterImpl( File webAppDirectory, String[] rejectedFilePaths ) + { + if ( rejectedFilePaths != null ) + { + this.rejectedFilePaths = Arrays.asList( rejectedFilePaths ); + } + else + { + this.rejectedFilePaths = new ArrayList<>(); + } + this.webAppDirIndex = webAppDirectory.getAbsolutePath().length() + 1; + } + + public boolean accept( File file ) + { + String effectiveRelativePath = buildRelativePath( file ); + return !( rejectedFilePaths.contains( effectiveRelativePath ) || file.isDirectory() ); + } + + private String buildRelativePath( File f ) + { + return f.getAbsolutePath().substring( webAppDirIndex ); + } + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarMojoTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarMojoTest.java new file mode 100644 index 000000000..87344ad26 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/AbstractWarMojoTest.java @@ -0,0 +1,332 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.apache.maven.execution.DefaultMavenExecutionRequest; +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.testing.AbstractMojoTestCase; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugins.war.AbstractWarMojo; +import org.apache.maven.plugins.war.stub.MavenProjectBasicStub; +import org.apache.maven.plugins.war.stub.WarOverlayStub; +import org.apache.maven.shared.filtering.MavenFileFilter; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.archiver.Archiver; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.util.FileUtils; +import org.sonatype.aether.RepositorySystemSession; + +public abstract class AbstractWarMojoTest + extends AbstractMojoTestCase +{ + + protected static final File OVERLAYS_TEMP_DIR = new File( getBasedir(), "target/test-overlays/" ); + + protected static final File OVERLAYS_ROOT_DIR = new File( getBasedir(), "target/test-classes/overlays/" ); + + protected static final String MANIFEST_PATH = "META-INF" + File.separator + "MANIFEST.MF"; + + protected abstract File getTestDirectory() + throws Exception; + + /** + * initialize required parameters + * + * @param mojo The mojo to be tested. + * @param filters The list of filters. + * @param classesDir The classes directory. + * @param webAppSource The webAppSource. + * @param webAppDir The webAppDir folder. + * @param project The Maven project. + * @throws Exception in case of errors + */ + protected void configureMojo( AbstractWarMojo mojo, List filters, File classesDir, File webAppSource, + File webAppDir, MavenProjectBasicStub project ) + throws Exception + { + setVariableValueToObject( mojo, "filters", filters ); + setVariableValueToObject( mojo, "mavenFileFilter", lookup( MavenFileFilter.class.getName() ) ); + setVariableValueToObject( mojo, "useJvmChmod", Boolean.TRUE ); + + MavenExecutionRequest request = new DefaultMavenExecutionRequest(); + request.setSystemProperties( System.getProperties() ); + + MavenSession mavenSession = + new MavenSession( (PlexusContainer) null, (RepositorySystemSession) null, request, null ); + setVariableValueToObject( mojo, "session", mavenSession ); + mojo.setClassesDirectory( classesDir ); + mojo.setWarSourceDirectory( webAppSource ); + mojo.setWebappDirectory( webAppDir ); + mojo.setProject( project ); + } + + /** + * create an isolated xml dir + * + * @param id The id. + * @param xmlFiles array of xml files. + * @return The created file. + * @throws Exception in case of errors. + */ + protected File createXMLConfigDir( String id, String[] xmlFiles ) + throws Exception + { + File xmlConfigDir = new File( getTestDirectory(), "/" + id + "-test-data/xml-config" ); + File XMLFile; + + createDir( xmlConfigDir ); + + if ( xmlFiles != null ) + { + for ( String o : xmlFiles ) + { + XMLFile = new File( xmlConfigDir, o ); + createFile( XMLFile ); + } + } + + return xmlConfigDir; + } + + /** + * Returns the webapp source directory for the specified id. + * + * @param id the id of the test + * @return the source directory for that test + * @throws Exception if an exception occurs + */ + protected File getWebAppSource( String id ) + throws Exception + { + return new File( getTestDirectory(), "/" + id + "-test-data/source" ); + } + + /** + * create an isolated web source with a sample jsp file + * + * @param id The id. + * @param createSamples Create example files yes or no. + * @return The created file. + * @throws Exception in case of errors. + */ + protected File createWebAppSource( String id, boolean createSamples ) + throws Exception + { + File webAppSource = getWebAppSource( id ); + if ( createSamples ) + { + File simpleJSP = new File( webAppSource, "pansit.jsp" ); + File jspFile = new File( webAppSource, "org/web/app/last-exile.jsp" ); + + createFile( simpleJSP ); + createFile( jspFile ); + } + return webAppSource; + } + + protected File createWebAppSource( String id ) + throws Exception + { + return createWebAppSource( id, true ); + } + + /** + * create a class directory with or without a sample class + * + * @param id The id. + * @param empty true to create a class files false otherwise. + * @return The created class file. + * @throws Exception in case of errors. + */ + protected File createClassesDir( String id, boolean empty ) + throws Exception + { + File classesDir = new File( getTestDirectory() + "/" + id + "-test-data/classes/" ); + + createDir( classesDir ); + + if ( !empty ) + { + createFile( new File( classesDir + "/sample-servlet.class" ) ); + } + + return classesDir; + } + + protected void createDir( File dir ) + { + if ( !dir.exists() ) + { + assertTrue( "can not create test dir: " + dir.toString(), dir.mkdirs() ); + } + } + + protected void createFile( File testFile, String body ) + throws Exception + { + createDir( testFile.getParentFile() ); + FileUtils.fileWrite( testFile.toString(), body ); + + assertTrue( "could not create file: " + testFile, testFile.exists() ); + } + + protected void createFile( File testFile ) + throws Exception + { + createFile( testFile, testFile.toString() ); + } + + /** + * Generates test war. + * Generates war with such a structure: + *
    + *
  • jsp + *
      + *
    • d + *
        + *
      • a.jsp
      • + *
      • b.jsp
      • + *
      • c.jsp
      • + *
      + *
    • + *
    • a.jsp
    • + *
    • b.jsp
    • + *
    • c.jsp
    • + *
    + *
  • + *
  • WEB-INF + *
      + *
    • classes + *
        + *
      • a.class
      • + *
      • b.class
      • + *
      • c.class
      • + *
      + *
    • + *
    • lib + *
        + *
      • a.jar
      • + *
      • b.jar
      • + *
      • c.jar
      • + *
      + *
    • + *
    • web.xml
    • + *
    + *
  • + *
+ * Each of the files will contain: id+'-'+path + * + * @param id the id of the overlay containing the full structure + * @return the war file + * @throws Exception if an error occurs + */ + protected File generateFullOverlayWar( String id ) + throws Exception + { + final File destFile = new File( OVERLAYS_TEMP_DIR, id + ".war" ); + if ( destFile.exists() ) + { + return destFile; + } + + // Archive was not yet created for that id so let's create it + final File rootDir = new File( OVERLAYS_ROOT_DIR, id ); + rootDir.mkdirs(); + String[] filePaths = new String[] { "jsp/d/a.jsp", "jsp/d/b.jsp", "jsp/d/c.jsp", "jsp/a.jsp", "jsp/b.jsp", + "jsp/c.jsp", "WEB-INF/classes/a.class", "WEB-INF/classes/b.class", "WEB-INF/classes/c.class", + "WEB-INF/lib/a.jar", "WEB-INF/lib/b.jar", "WEB-INF/lib/c.jar", "WEB-INF/web.xml" }; + + for ( String filePath : filePaths ) + { + createFile( new File( rootDir, filePath ), id + "-" + filePath ); + } + + createArchive( rootDir, destFile ); + return destFile; + } + + // Overlay utilities + + /** + * Builds a test overlay. + * + * @param id the id of the overlay (see test/resources/overlays) + * @return a test war artifact with the content of the given test overlay + */ + protected ArtifactStub buildWarOverlayStub( String id ) + { + // Create war file + final File destFile = new File( OVERLAYS_TEMP_DIR, id + ".war" ); + if ( !destFile.exists() ) + { + createArchive( new File( OVERLAYS_ROOT_DIR, id ), destFile ); + } + + return new WarOverlayStub( getBasedir(), id, destFile ); + } + + protected File getOverlayFile( String id, String filePath ) + { + final File overlayDir = new File( OVERLAYS_ROOT_DIR, id ); + final File file = new File( overlayDir, filePath ); + + // Make sure the file exists + assertTrue( "Overlay file " + filePath + " does not exist for overlay " + id + " at " + file.getAbsolutePath(), + file.exists() ); + return file; + + } + + protected void createArchive( final File directory, final File destinationFile ) + { + try + { + // WarArchiver archiver = new WarArchiver(); + + Archiver archiver = new JarArchiver(); + + archiver.setDestFile( destinationFile ); + archiver.addDirectory( directory ); + + // archiver.setWebxml( new File(directory, "WEB-INF/web.xml")); + + // create archive + archiver.createArchive(); + + } + catch ( ArchiverException e ) + { + e.printStackTrace(); + fail( "Failed to create overlay archive " + e.getMessage() ); + } + catch ( IOException e ) + { + e.printStackTrace(); + fail( "Unexpected exception " + e.getMessage() ); + } + } + +} \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoFilteringTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoFilteringTest.java new file mode 100644 index 000000000..9997f4d68 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoFilteringTest.java @@ -0,0 +1,209 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.BufferedReader; +import java.io.File; +import java.io.StringReader; +import java.util.LinkedList; +import java.util.List; + +import org.apache.maven.plugins.war.stub.MavenProjectBasicStub; +import org.apache.maven.plugins.war.stub.ResourceStub; +import org.codehaus.plexus.util.FileUtils; + +/** + * @author Olivier Lamy + * @since 21 juil. 2008 + */ +public class WarExplodedMojoFilteringTest + extends AbstractWarExplodedMojoTest +{ + + protected File getPomFile() + { + return new File( getBasedir(), "/target/test-classes/unit/warexplodedmojo/plugin-config.xml" ); + } + + protected File getTestDirectory() + { + return new File( getBasedir(), "target/test-classes/unit/warexplodedmojo/test-dir" ); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithResourceFiltering() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithResourceFiltering"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, false ); + File webAppResource = new File( getTestDirectory(), testId + "-test-data/resources" ); + File sampleResource = new File( webAppResource, "custom-setting.cfg" ); + File sampleResourceWDir = new File( webAppResource, "custom-config/custom-setting.cfg" ); + List filterList = new LinkedList<>(); + ResourceStub[] resources = new ResourceStub[] { new ResourceStub() }; + + createFile( sampleResource ); + createFile( sampleResourceWDir ); + + String ls = System.getProperty( "line.separator" ); + final String comment = "# this is comment created by author@somewhere@"; + // prepare web resources + String content = comment + ls; + content += "system_key_1=${user.dir}" + ls; + content += "system_key_2=@user.dir@" + ls; + content += "project_key_1=${is_this_simple}" + ls; + content += "project_key_2=@is_this_simple@" + ls; + content += "project_name_1=${project.name}" + ls; + content += "project_name_2=@project.name@" + ls; + content += "system_property_1=${system.property}" + ls; + content += "system_property_2=@system.property@" + ls; + FileUtils.fileWrite( sampleResourceWDir.getAbsolutePath(), content ); + FileUtils.fileWrite( sampleResource.getAbsolutePath(), content ); + + System.setProperty( "system.property", "system-property-value" ); + + // configure mojo + project.addProperty( "is_this_simple", "i_think_so" ); + resources[0].setDirectory( webAppResource.getAbsolutePath() ); + resources[0].setFiltering( true ); + this.configureMojo( mojo, filterList, classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "webResources", resources ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedResourceFile = new File( webAppDirectory, "custom-setting.cfg" ); + File expectedResourceWDirFile = new File( webAppDirectory, "custom-config/custom-setting.cfg" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "resource file not found:" + expectedResourceFile.toString(), expectedResourceFile.exists() ); + assertTrue( "resource file with dir not found:" + expectedResourceWDirFile.toString(), + expectedResourceWDirFile.exists() ); + + // validate filtered file + content = FileUtils.fileRead( expectedResourceWDirFile ); + BufferedReader reader = new BufferedReader( new StringReader( content ) ); + + assertEquals( "error in filtering using System Properties", comment, reader.readLine() ); + + String line = reader.readLine(); + System.out.println( " line " + line ); + System.out.println( " need " + System.getProperty( "user.dir" ) ); + assertEquals( "error in filtering using System properties", "system_key_1=" + System.getProperty( "user.dir" ), + line ); + line = reader.readLine(); + System.out.println( " line " + line ); + assertEquals( "error in filtering using System properties", "system_key_2=" + System.getProperty( "user.dir" ), + line ); + + assertEquals( "error in filtering using project properties", "project_key_1=i_think_so", reader.readLine() ); + assertEquals( "error in filtering using project properties", "project_key_2=i_think_so", reader.readLine() ); + + assertEquals( "error in filtering using project properties", "project_name_1=Test Project ", reader.readLine() ); + assertEquals( "error in filtering using project properties", "project_name_2=Test Project ", reader.readLine() ); + + assertEquals( "error in filtering using System properties", "system_property_1=system-property-value", + reader.readLine() ); + assertEquals( "error in filtering using System properties", "system_property_2=system-property-value", + reader.readLine() ); + + // update property, and generate again + System.setProperty( "system.property", "new-system-property-value" ); + this.configureMojo( mojo, filterList, classesDir, webAppSource, webAppDirectory, project ); + + mojo.execute(); + + // validate filtered file + content = FileUtils.fileRead( expectedResourceWDirFile ); + reader = new BufferedReader( new StringReader( content ) ); + + assertEquals( "error in filtering using System Properties", comment, reader.readLine() ); + + assertEquals( "error in filtering using System properties", "system_key_1=" + System.getProperty( "user.dir" ), + reader.readLine() ); + assertEquals( "error in filtering using System properties", "system_key_2=" + System.getProperty( "user.dir" ), + reader.readLine() ); + + assertEquals( "error in filtering using project properties", "project_key_1=i_think_so", reader.readLine() ); + assertEquals( "error in filtering using project properties", "project_key_2=i_think_so", reader.readLine() ); + + assertEquals( "error in filtering using project properties", "project_name_1=Test Project ", reader.readLine() ); + assertEquals( "error in filtering using project properties", "project_name_2=Test Project ", reader.readLine() ); + + assertEquals( "error in filtering using System properties", "system_property_1=new-system-property-value", + reader.readLine() ); + assertEquals( "error in filtering using System properties", "system_property_2=new-system-property-value", + reader.readLine() ); + + // update property, and generate again + File filterFile = new File( getTestDirectory(), testId + "-test-data/filters/filter.properties" ); + createFile( filterFile ); + filterList.add( filterFile.getAbsolutePath() ); + content += "resource_key_1=${resource_value_1}\n"; + content += "resource_key_2=@resource_value_2@\n" + content; + FileUtils.fileWrite( sampleResourceWDir.getAbsolutePath(), content ); + FileUtils.fileWrite( sampleResource.getAbsolutePath(), content ); + String filterContent = "resource_value_1=this_is_filtered\n"; + filterContent += "resource_value_2=this_is_filtered"; + FileUtils.fileWrite( filterFile.getAbsolutePath(), filterContent ); + + mojo.execute(); + + // validate filtered file + content = FileUtils.fileRead( expectedResourceWDirFile ); + reader = new BufferedReader( new StringReader( content ) ); + + assertEquals( "error in filtering using System Properties", comment, reader.readLine() ); + + assertEquals( "error in filtering using System properties", "system_key_1=" + System.getProperty( "user.dir" ), + reader.readLine() ); + assertEquals( "error in filtering using System properties", "system_key_2=" + System.getProperty( "user.dir" ), + reader.readLine() ); + + assertEquals( "error in filtering using project properties", "project_key_1=i_think_so", reader.readLine() ); + assertEquals( "error in filtering using project properties", "project_key_2=i_think_so", reader.readLine() ); + + assertEquals( "error in filtering using project properties", "project_name_1=Test Project ", reader.readLine() ); + assertEquals( "error in filtering using project properties", "project_name_2=Test Project ", reader.readLine() ); + + assertEquals( "error in filtering using System properties", "system_property_1=new-system-property-value", + reader.readLine() ); + assertEquals( "error in filtering using System properties", "system_property_2=new-system-property-value", + reader.readLine() ); + + assertEquals( "error in filtering using filter files", "resource_key_1=this_is_filtered", reader.readLine() ); + assertEquals( "error in filtering using filter files", "resource_key_2=this_is_filtered", reader.readLine() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedResourceFile.delete(); + expectedResourceWDirFile.delete(); + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoTest.java new file mode 100644 index 000000000..22ff280de --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarExplodedMojoTest.java @@ -0,0 +1,1055 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import static org.junit.Assert.assertNotEquals; + +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugins.war.stub.AarArtifactStub; +import org.apache.maven.plugins.war.stub.EJBArtifactStub; +import org.apache.maven.plugins.war.stub.EJBArtifactStubWithClassifier; +import org.apache.maven.plugins.war.stub.EJBClientArtifactStub; +import org.apache.maven.plugins.war.stub.IncludeExcludeWarArtifactStub; +import org.apache.maven.plugins.war.stub.JarArtifactStub; +import org.apache.maven.plugins.war.stub.MarArtifactStub; +import org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub; +import org.apache.maven.plugins.war.stub.MavenProjectBasicStub; +import org.apache.maven.plugins.war.stub.PARArtifactStub; +import org.apache.maven.plugins.war.stub.ResourceStub; +import org.apache.maven.plugins.war.stub.TLDArtifactStub; +import org.apache.maven.plugins.war.stub.WarArtifactStub; +import org.apache.maven.plugins.war.stub.XarArtifactStub; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.LinkedList; +import java.util.Locale; + +public class WarExplodedMojoTest + extends AbstractWarExplodedMojoTest +{ + + protected File getPomFile() + { + return new File( getBasedir(), "/target/test-classes/unit/warexplodedmojo/plugin-config.xml" ); + } + + protected File getTestDirectory() + { + return new File( getBasedir(), "target/test-classes/unit/warexplodedmojo/test-dir" ); + } + + /** + * @throws Exception in case of an error. + */ + public void testSimpleExplodedWar() + throws Exception + { + // setup test data + String testId = "SimpleExplodedWar"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, false ); + File webAppResource = new File( getTestDirectory(), testId + "-resources" ); + File webAppDirectory = new File( getTestDirectory(), testId ); + File sampleResource = new File( webAppResource, "pix/panis_na.jpg" ); + ResourceStub[] resources = new ResourceStub[] { new ResourceStub() }; + + createFile( sampleResource ); + + assertTrue( "sampeResource not found", sampleResource.exists() ); + + // configure mojo + resources[0].setDirectory( webAppResource.getAbsolutePath() ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "webResources", resources ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedWebResourceFile = new File( webAppDirectory, "pix/panis_na.jpg" ); + File expectedWEBINFDir = new File( webAppDirectory, "WEB-INF" ); + File expectedMETAINFDir = new File( webAppDirectory, "META-INF" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "resources doesn't exist: " + expectedWebResourceFile, expectedWebResourceFile.exists() ); + assertTrue( "WEB-INF not found", expectedWEBINFDir.exists() ); + assertTrue( "META-INF not found", expectedMETAINFDir.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedWebResourceFile.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testSimpleExplodedWarWTargetPath() + throws Exception + { + // setup test data + String testId = "SimpleExplodedWar"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, false ); + File webAppResource = new File( getTestDirectory(), "resources" ); + File webAppDirectory = new File( getTestDirectory(), testId ); + File sampleResource = new File( webAppResource, "pix/panis_na.jpg" ); + ResourceStub[] resources = new ResourceStub[] { new ResourceStub() }; + + createFile( sampleResource ); + + // configure mojo + resources[0].setDirectory( webAppResource.getAbsolutePath() ); + resources[0].setTargetPath( "targetPath" ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "webResources", resources ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedWebResourceFile = new File( webAppDirectory, "targetPath/pix/panis_na.jpg" ); + File expectedWEBINFDir = new File( webAppDirectory, "WEB-INF" ); + File expectedMETAINFDir = new File( webAppDirectory, "META-INF" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "resources doesn't exist: " + expectedWebResourceFile, expectedWebResourceFile.exists() ); + assertTrue( "WEB-INF not found", expectedWEBINFDir.exists() ); + assertTrue( "META-INF not found", expectedMETAINFDir.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedWebResourceFile.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithCustomWebXML() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithCustomWebXML"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + File webAppDirectory = new File( getTestDirectory(), testId ); + + // configure mojo + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedWEBXMLFile = new File( webAppDirectory, "WEB-INF/web.xml" ); + File expectedMETAINFDir = new File( webAppDirectory, "META-INF" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "WEB XML not found: " + expectedWEBXMLFile.toString(), expectedWEBXMLFile.exists() ); + assertTrue( "META-INF not found", expectedMETAINFDir.exists() ); + assertEquals( "WEB XML not correct", mojo.getWebXml().toString(), FileUtils.fileRead( expectedWEBXMLFile ) ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedWEBXMLFile.delete(); + expectedMETAINFDir.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithContainerConfigXML() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithContainerConfigXML"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File classesDir = createClassesDir( testId, true ); + File webAppSource = createWebAppSource( testId ); + File xmlSource = createXMLConfigDir( testId, new String[] { "config.xml" } ); + File webAppDirectory = new File( getTestDirectory(), testId ); + + // configure mojo + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.setContainerConfigXML( new File( xmlSource, "config.xml" ) ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedContainerConfigXMLFile = new File( webAppDirectory, "META-INF/config.xml" ); + File expectedWEBINFDir = new File( webAppDirectory, "WEB-INF" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "WEB-INF not found", expectedWEBINFDir.exists() ); + assertTrue( "Container Config XML not found:" + expectedContainerConfigXMLFile.toString(), + expectedContainerConfigXMLFile.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedContainerConfigXMLFile.delete(); + expectedWEBINFDir.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithSimpleExternalWARFile() + throws Exception + { + // setup test data + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + WarArtifactStub warArtifact = new WarArtifactStub( getBasedir() ); + + String testId = "ExplodedWar_WithSimpleExternalWARFile"; + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File workDirectory = new File( getTestDirectory(), "/war/work-" + testId ); + File simpleWarFile = warArtifact.getFile(); + + assertTrue( "simple war not found: " + simpleWarFile.toString(), simpleWarFile.exists() ); + + createDir( workDirectory ); + + // configure mojo + project.addArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "workDirectory", workDirectory ); + mojo.execute(); + + // validate operation - META-INF is automatically excluded so remove the file from the list + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedWEBXMLFile = new File( webAppDirectory, "WEB-INF/web.xml" ); + File expectedWARFile = new File( webAppDirectory, "/org/sample/company/test.jsp" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + // check simple.war in the unit test dir under resources to verify the list of files + assertTrue( "web xml not found: " + expectedWEBXMLFile.toString(), expectedWEBXMLFile.exists() ); + assertTrue( "war file not found: " + expectedWARFile.toString(), expectedWARFile.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedWEBXMLFile.delete(); + expectedWARFile.delete(); + } + + /** + * Merge a dependent WAR when a file in the war source directory overrides one found in the WAR. + * @throws Exception in case of an error. + */ + public void testExplodedWarMergeWarLocalFileOverride() + throws Exception + { + // setup test data + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + WarArtifactStub warArtifact = new WarArtifactStub( getBasedir() ); + + String testId = "testExplodedWarMergeWarLocalFileOverride"; + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = getWebAppSource( testId ); + File simpleJSP = new File( webAppSource, "org/sample/company/test.jsp" ); + createFile( simpleJSP ); + + File workDirectory = new File( getTestDirectory(), "/war/work-" + testId ); + createDir( workDirectory ); + + File classesDir = createClassesDir( testId, true ); + + // configure mojo + project.addArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "workDirectory", workDirectory ); + mojo.execute(); + + // validate operation + File expectedFile = new File( webAppDirectory, "/org/sample/company/test.jsp" ); + + assertTrue( "file not found: " + expectedFile.toString(), expectedFile.exists() ); + assertEquals( "file incorrect", simpleJSP.toString(), FileUtils.fileRead( expectedFile ) ); + + // check when the merged war file is newer - so set an old time on the local file + long time = new SimpleDateFormat( "yyyy-MM-dd", Locale.US ).parse( "2005-1-1" ).getTime(); + simpleJSP.setLastModified( time ); + expectedFile.setLastModified( time ); + + project.addArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "workDirectory", workDirectory ); + mojo.execute(); + + assertTrue( "file not found: " + expectedFile.toString(), expectedFile.exists() ); + assertEquals( "file incorrect", simpleJSP.toString(), FileUtils.fileRead( expectedFile ) ); + + // house keeping + expectedFile.delete(); + } + + // The last modified thingy behavior is not applicable anymore. This is the only test that + // has been removed. + // /** + // * Merge a dependent WAR that gets updated since the last run. + // */ + // public void testExplodedWarMergeWarUpdated() + // throws Exception + // { + // // setup test data + // MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + // WarArtifactStub warArtifact = new WarArtifactStub( getBasedir() ); + // + // String testId = "testExplodedWarMergeWarUpdated"; + // File webAppDirectory = new File( getTestDirectory(), testId ); + // FileUtils.deleteDirectory( webAppDirectory ); + // + // File webAppSource = getWebAppSource( testId ); + // + // File workDirectory = new File( getTestDirectory(), "/war/work-" + testId ); + // createDir( workDirectory ); + // + // File classesDir = createClassesDir( testId, true ); + // + // // configure mojo + // project.addArtifact( warArtifact ); + // this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + // setVariableValueToObject( mojo, "workDirectory", workDirectory ); + // mojo.execute(); + // + // // validate operation + // File expectedFile = new File( webAppDirectory, "/org/sample/company/test.jsp" ); + // + // assertTrue( "file not found: " + expectedFile.toString(), expectedFile.exists() ); + // assertEquals( "file incorrect", "", FileUtils.fileRead( expectedFile ) ); + // + // // update file, so the local one is older + // warArtifact.setFile( new File( warArtifact.getFile().getParentFile(), "simple-updated.war" ) ); + // + // mojo.execute(); + // + // assertTrue( "file not found: " + expectedFile.toString(), expectedFile.exists() ); + // assertEquals( "file incorrect", "updated\n", FileUtils.fileRead( expectedFile ) ); + // + // // update file, so the local one is newer + // warArtifact.setFile( new File( warArtifact.getFile().getParentFile(), "simple.war" ) ); + // + // mojo.execute(); + // + // assertTrue( "file not found: " + expectedFile.toString(), expectedFile.exists() ); + // assertEquals( "file incorrect", "updated\n", FileUtils.fileRead( expectedFile ) ); + // + // // house keeping + // expectedFile.delete(); + // } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithEJB() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithEJB"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + EJBArtifactStub ejbArtifact = new EJBArtifactStub( getBasedir() ); + File ejbFile = ejbArtifact.getFile(); + + assertTrue( "ejb jar not found: " + ejbFile.toString(), ejbFile.exists() ); + + // configure mojo + project.addArtifact( ejbArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedEJBArtifact = new File( webAppDirectory, "WEB-INF/lib/ejbartifact-0.0-Test.jar" ); + // File expectedEJBArtifact = new File( webAppDirectory, "WEB-INF/lib/ejbartifact-0.0-Test.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "ejb artifact not found: " + expectedEJBArtifact.toString(), expectedEJBArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedEJBArtifact.delete(); + } + + public void testExplodedWarWithJar() + throws Exception + { + // setup test data + String testId = "ExplodedWarWithJar"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE, "jar" ); + ArtifactStub jarArtifact = new JarArtifactStub( getBasedir(), artifactHandler ); + File jarFile = jarArtifact.getFile(); + + assertTrue( "jar not found: " + jarFile.toString(), jarFile.exists() ); + + // configure mojo + project.addArtifact( jarArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedJarArtifact = new File( webAppDirectory, "WEB-INF/lib/jarartifact-0.0-Test.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "jar artifact not found: " + expectedJarArtifact.toString(), expectedJarArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedJarArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithEJBClient() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithEJB"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + EJBClientArtifactStub ejbArtifact = new EJBClientArtifactStub( getBasedir() ); + File ejbFile = ejbArtifact.getFile(); + + assertTrue( "ejb jar not found: " + ejbFile.toString(), ejbFile.exists() ); + + // configure mojo + project.addArtifact( ejbArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedEJBArtifact = new File( webAppDirectory, "WEB-INF/lib/ejbclientartifact-0.0-Test-client.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "ejb artifact not found: " + expectedEJBArtifact.toString(), expectedEJBArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedEJBArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithTLD() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithTLD"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + TLDArtifactStub tldArtifact = new TLDArtifactStub( getBasedir() ); + File tldFile = tldArtifact.getFile(); + + assertTrue( "tld jar not found: " + tldFile.getAbsolutePath(), tldFile.exists() ); + + // configure mojo + project.addArtifact( tldArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedTLDArtifact = new File( webAppDirectory, "WEB-INF/tld/tldartifact-0.0-Test.tld" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "tld artifact not found: " + expectedTLDArtifact.toString(), expectedTLDArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedTLDArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithPAR() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithPAR"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + PARArtifactStub parartifact = new PARArtifactStub( getBasedir() ); + File parFile = parartifact.getFile(); + + assertTrue( "par not found: " + parFile.getAbsolutePath(), parFile.exists() ); + + // configure mojo + project.addArtifact( parartifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedPARArtifact = new File( webAppDirectory, "WEB-INF/lib/parartifact-0.0-Test.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "par artifact not found: " + expectedPARArtifact.toString(), expectedPARArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedPARArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWarWithAar() + throws Exception + { + // setup test data + String testId = "ExplodedWarWithAar"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + // Fake here since the aar artifact handler does not exist: no biggie + ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE, "jar" ); + ArtifactStub aarArtifact = new AarArtifactStub( getBasedir(), artifactHandler ); + File aarFile = aarArtifact.getFile(); + + assertTrue( "jar not found: " + aarFile.toString(), aarFile.exists() ); + + // configure mojo + project.addArtifact( aarArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedJarArtifact = new File( webAppDirectory, "WEB-INF/services/aarartifact-0.0-Test.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "jar artifact not found: " + expectedJarArtifact.toString(), expectedJarArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedJarArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWarWithMar() + throws Exception + { + // setup test data + String testId = "ExplodedWarWithMar"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + // Fake here since the mar artifact handler does not exist: no biggie + ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE, "jar" ); + ArtifactStub marArtifact = new MarArtifactStub( getBasedir(), artifactHandler ); + File marFile = marArtifact.getFile(); + + assertTrue( "jar not found: " + marFile.toString(), marFile.exists() ); + + // configure mojo + project.addArtifact( marArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedJarArtifact = new File( webAppDirectory, "WEB-INF/modules/marartifact-0.0-Test.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "jar artifact not found: " + expectedJarArtifact.toString(), expectedJarArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedJarArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWarWithXar() + throws Exception + { + // setup test data + String testId = "ExplodedWarWithXar"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + // Fake here since the xar artifact handler does not exist: no biggie + ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE, "jar" ); + ArtifactStub xarArtifact = new XarArtifactStub( getBasedir(), artifactHandler ); + File xarFile = xarArtifact.getFile(); + + assertTrue( "jar not found: " + xarFile.toString(), xarFile.exists() ); + + // configure mojo + project.addArtifact( xarArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedJarArtifact = new File( webAppDirectory, "WEB-INF/extensions/xarartifact-0.0-Test.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "jar artifact not found: " + expectedJarArtifact.toString(), expectedJarArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedJarArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithDuplicateDependencies() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithDuplicateDependencies"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + EJBArtifactStub ejbArtifact = new EJBArtifactStub( getBasedir() ); + EJBArtifactStub ejbArtifactDup = new EJBArtifactStub( getBasedir() ); + File ejbFile = ejbArtifact.getFile(); + + // ejbArtifact has a hard coded file, only one assert is needed + assertTrue( "ejb not found: " + ejbFile.getAbsolutePath(), ejbFile.exists() ); + + // configure mojo + ejbArtifact.setGroupId( "org.sample.ejb" ); + ejbArtifactDup.setGroupId( "org.dup.ejb" ); + project.addArtifact( ejbArtifact ); + project.addArtifact( ejbArtifactDup ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedEJBArtifact = new File( webAppDirectory, "WEB-INF/lib/org.sample.ejb-ejbartifact-0.0-Test.jar" ); + File expectedEJBDupArtifact = new File( webAppDirectory, "WEB-INF/lib/org.dup.ejb-ejbartifact-0.0-Test.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "ejb artifact not found: " + expectedEJBArtifact.toString(), expectedEJBArtifact.exists() ); + assertTrue( "ejb dup artifact not found: " + expectedEJBDupArtifact.toString(), expectedEJBDupArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedEJBArtifact.delete(); + expectedEJBDupArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_DuplicateWithClassifier() + throws Exception + { + // setup test data + String testId = "ExplodedWar_DuplicateWithClassifier"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + EJBArtifactStub ejbArtifact = new EJBArtifactStub( getBasedir() ); + EJBArtifactStubWithClassifier ejbArtifactDup = new EJBArtifactStubWithClassifier( getBasedir() ); + + File ejbFile = ejbArtifact.getFile(); + + // ejbArtifact has a hard coded file, only one assert is needed + assertTrue( "ejb not found: " + ejbFile.getAbsolutePath(), ejbFile.exists() ); + + // configure mojo + + ejbArtifact.setGroupId( "org.sample.ejb" ); + ejbArtifactDup.setGroupId( "org.sample.ejb" ); + + ejbArtifactDup.setClassifier( "classifier" ); + + project.addArtifact( ejbArtifact ); + project.addArtifact( ejbArtifactDup ); + + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedEJBArtifact = new File( webAppDirectory, "WEB-INF/lib/ejbartifact-0.0-Test.jar" ); + File expectedEJBDupArtifact = new File( webAppDirectory, "WEB-INF/lib/ejbartifact-0.0-Test-classifier.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "ejb artifact not found: " + expectedEJBArtifact.toString(), expectedEJBArtifact.exists() ); + assertTrue( "ejb dup artifact not found: " + expectedEJBDupArtifact.toString(), expectedEJBDupArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedEJBArtifact.delete(); + expectedEJBDupArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithClasses() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithClasses"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, false ); + + // configure mojo + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedClass = new File( webAppDirectory, "WEB-INF/classes/sample-servlet.class" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "classes not found: " + expectedClass.toString(), expectedClass.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedClass.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithSourceIncludeExclude() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithSourceIncludeExclude"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File webAppDirectory = new File( getTestDirectory(), testId ); + + // configure mojo + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "warSourceIncludes", "**/*sit.jsp" ); + setVariableValueToObject( mojo, "warSourceExcludes", "**/last*.*" ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedWEBXMLDir = new File( webAppDirectory, "WEB-INF" ); + File expectedMETAINFDir = new File( webAppDirectory, "META-INF" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertFalse( "source files found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "WEB XML not found: " + expectedWEBXMLDir.toString(), expectedWEBXMLDir.exists() ); + assertTrue( "META-INF not found", expectedMETAINFDir.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedWEBXMLDir.delete(); + expectedMETAINFDir.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWar_WithWarDependencyIncludeExclude() + throws Exception + { + // setup test data + String testId = "ExplodedWar_WithWarDependencyIncludeExclude"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + IncludeExcludeWarArtifactStub includeexcludeWarArtifact = new IncludeExcludeWarArtifactStub( getBasedir() ); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File workDirectory = new File( getTestDirectory(), "/war/work-" + testId ); + File includeExcludeWarFile = includeexcludeWarArtifact.getFile(); + + assertTrue( "war not found: " + includeExcludeWarFile.toString(), includeExcludeWarFile.exists() ); + + createDir( workDirectory ); + + // configure mojo + project.addArtifact( includeexcludeWarArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "dependentWarIncludes", "**/*Include.jsp,**/*.xml" ); + setVariableValueToObject( mojo, "dependentWarExcludes", "**/*Exclude*,**/MANIFEST.MF" ); + setVariableValueToObject( mojo, "workDirectory", workDirectory ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + File expectedManifestFile = new File( webAppDirectory, "META-INF/MANIFEST.MF" ); + File expectedWEBXMLFile = new File( webAppDirectory, "WEB-INF/web.xml" ); + File expectedIncludedWARFile = new File( webAppDirectory, "/org/sample/company/testInclude.jsp" ); + File expectedExcludedWarfile = new File( webAppDirectory, "/org/sample/companyExclude/test.jsp" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + // check include-exclude.war in the unit test dir under resources to verify the list of files + assertTrue( "web xml not found: " + expectedWEBXMLFile.toString(), expectedWEBXMLFile.exists() ); + assertFalse( "manifest file found: " + expectedManifestFile.toString(), expectedManifestFile.exists() ); + assertTrue( "war file not found: " + expectedIncludedWARFile.toString(), expectedIncludedWARFile.exists() ); + assertFalse( "war file not found: " + expectedExcludedWarfile.toString(), expectedExcludedWarfile.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedManifestFile.delete(); + expectedWEBXMLFile.delete(); + expectedIncludedWARFile.delete(); + expectedExcludedWarfile.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWarWithSourceModificationCheck() + throws Exception + { + // setup test data + String testId = "ExplodedWarWithSourceModificationCheck"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, false ); + File webAppDirectory = new File( getTestDirectory(), testId ); + + // configure mojo + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + + // destination file is already created manually containing an "error" string + // source is newer than the destination file + mojo.execute(); + + // validate operation + + File expectedWEBINFDir = new File( webAppDirectory, "WEB-INF" ); + File expectedMETAINFDir = new File( webAppDirectory, "META-INF" ); + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "WEB-INF not found", expectedWEBINFDir.exists() ); + assertTrue( "META-INF not found", expectedMETAINFDir.exists() ); + + // 1st phase destination is older than source + // destination starts with a value of error replaced with a blank source + assertNotEquals( "source files not updated with new copy: " + expectedWebSourceFile.toString(), + "error", FileUtils.fileRead( expectedWebSourceFile ) ); + + // TODO: uncomment when lastModified problem is resolved + // FileWriter writer = new FileWriter(expectedWebSourceFile); + // + // // 2nd phase destination is newer than source + // // destination should not be replaced with an blank source + // writer.write("newdata"); + // mojo.execute(); + // reader = new FileReader(expectedWebSourceFile); + // reader.read(data); + // assertTrue("source file updated with old copy: " + // +expectedWebSourceFile.toString(),String.valueOf(data).equals("newdata") ); } + + // house keeping + expectedWEBINFDir.delete(); + expectedMETAINFDir.delete(); + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWarWithOutputFileNameMapping() + throws Exception + { + // setup test data + String testId = "ExplodedWarWithFileNameMapping"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE, "jar" ); + ArtifactStub jarArtifact = new JarArtifactStub( getBasedir(), artifactHandler ); + File jarFile = jarArtifact.getFile(); + + assertTrue( "jar not found: " + jarFile.toString(), jarFile.exists() ); + + // configure mojo + project.addArtifact( jarArtifact ); + mojo.setOutputFileNameMapping( "@{artifactId}@.@{extension}@" ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedJarArtifact = new File( webAppDirectory, "WEB-INF/lib/jarartifact.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "jar artifact not found: " + expectedJarArtifact.toString(), expectedJarArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedJarArtifact.delete(); + } + + /** + * @throws Exception in case of an error. + */ + public void testExplodedWarWithOutputFileNameMappingAndDuplicateDependencies() + throws Exception + { + // setup test data + String testId = "ExplodedWarWithFileNameMappingAndDuplicateDependencies"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + File webAppDirectory = new File( getTestDirectory(), testId ); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + EJBArtifactStub ejbArtifact = new EJBArtifactStub( getBasedir() ); + EJBArtifactStub ejbArtifactDup = new EJBArtifactStub( getBasedir() ); + File ejbFile = ejbArtifact.getFile(); + + // ejbArtifact has a hard coded file, only one assert is needed + assertTrue( "ejb not found: " + ejbFile.getAbsolutePath(), ejbFile.exists() ); + + // configure mojo + ejbArtifact.setGroupId( "org.sample.ejb" ); + ejbArtifactDup.setGroupId( "org.dup.ejb" ); + project.addArtifact( ejbArtifact ); + project.addArtifact( ejbArtifactDup ); + mojo.setOutputFileNameMapping( "@{artifactId}@.@{extension}@" ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + // final name form is -. + File expectedEJBArtifact = new File( webAppDirectory, "WEB-INF/lib/org.sample.ejb-ejbartifact.jar" ); + File expectedEJBDupArtifact = new File( webAppDirectory, "WEB-INF/lib/org.dup.ejb-ejbartifact.jar" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "ejb artifact not found: " + expectedEJBArtifact.toString(), expectedEJBArtifact.exists() ); + assertTrue( "ejb dup artifact not found: " + expectedEJBDupArtifact.toString(), expectedEJBDupArtifact.exists() ); + + // house keeping + expectedWebSourceFile.delete(); + expectedWebSource2File.delete(); + expectedEJBArtifact.delete(); + expectedEJBDupArtifact.delete(); + } + + /* --------------------- 2.1 Overlay tests ----------------------------------- */ + + /*---------------------------*/ + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarInPlaceMojoTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarInPlaceMojoTest.java new file mode 100644 index 000000000..d93433ccd --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarInPlaceMojoTest.java @@ -0,0 +1,90 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugins.war.stub.MavenProjectBasicStub; +import org.apache.maven.plugins.war.stub.ResourceStub; + +import java.io.File; +import java.util.LinkedList; + +public class WarInPlaceMojoTest + extends AbstractWarMojoTest +{ + protected static final String pomFilePath = getBasedir() + + "/target/test-classes/unit/warexplodedinplacemojo/plugin-config.xml"; + + protected File getTestDirectory() + throws Exception + { + return new File( getBasedir(), "target/test-classes/unit/warexplodedinplacemojo/test-dir" ); + } + + private WarInPlaceMojo mojo; + + public void setUp() + throws Exception + { + super.setUp(); + + mojo = (WarInPlaceMojo) lookupMojo( "inplace", pomFilePath ); + assertNotNull( mojo ); + } + + public void testEnvironment() + throws Exception + { + // see setUp + } + + public void testSimpleExplodedInplaceWar() + throws Exception + { + // setup test data + String testId = "SimpleExplodedInplaceWar"; + MavenProjectBasicStub project = new MavenProjectBasicStub(); + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File webAppResource = new File( getTestDirectory(), "resources" ); + File sampleResource = new File( webAppResource, "pix/panis_na.jpg" ); + ResourceStub[] resources = new ResourceStub[] { new ResourceStub() }; + + createFile( sampleResource ); + + // configure mojo + resources[0].setDirectory( webAppResource.getAbsolutePath() ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, null, project ); + setVariableValueToObject( mojo, "webResources", resources ); + mojo.execute(); + + // validate operation + File expectedWebSourceFile = new File( webAppSource, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppSource, "org/web/app/last-exile.jsp" ); + File expectedWebResourceFile = new File( webAppSource, "pix/panis_na.jpg" ); + File expectedWEBINFDir = new File( webAppSource, "WEB-INF" ); + File expectedMETAINFDir = new File( webAppSource, "META-INF" ); + + assertTrue( "source files not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source files not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + assertTrue( "resources doesn't exist: " + expectedWebResourceFile, expectedWebResourceFile.exists() ); + assertTrue( "WEB-INF not found", expectedWEBINFDir.exists() ); + assertTrue( "META-INF not found", expectedMETAINFDir.exists() ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarMojoTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarMojoTest.java new file mode 100644 index 000000000..7a313ec25 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarMojoTest.java @@ -0,0 +1,535 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.stub.JarArtifactStub; +import org.apache.maven.plugins.war.stub.MavenProject4CopyConstructor; +import org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub; +import org.apache.maven.plugins.war.stub.ProjectHelperStub; +import org.apache.maven.plugins.war.stub.WarArtifact4CCStub; +import org.codehaus.plexus.util.IOUtil; + +/** + * comprehensive test on buildExplodedWebApp is done on WarExplodedMojoTest + */ +public class WarMojoTest + extends AbstractWarMojoTest +{ + WarMojo mojo; + + private static File pomFile = new File( getBasedir(), + "target/test-classes/unit/warmojotest/plugin-config-primary-artifact.xml" ); + + protected File getTestDirectory() + { + return new File( getBasedir(), "target/test-classes/unit/warmojotest" ); + } + + public void setUp() + throws Exception + { + super.setUp(); + mojo = (WarMojo) lookupMojo( "war", pomFile ); + } + + public void testEnvironment() + throws Exception + { + // see setup + } + + public void testSimpleWar() + throws Exception + { + String testId = "SimpleWar"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple.war" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "WEB-INF/web.xml", "pansit.jsp", + "org/web/app/last-exile.jsp", "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, + mojo.getWebXml().toString(), null, null, null, null } ); + } + + public void testSimpleWarPackagingExcludeWithIncludesRegEx() + throws Exception + { + String testId = "SimpleWarPackagingExcludeWithIncludesRegEx"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + setVariableValueToObject( mojo, "packagingIncludes", "%regex[(.(?!exile))+]" ); +// setVariableValueToObject( mojo, "packagingIncludes", "%regex" ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple.war" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "WEB-INF/web.xml", "pansit.jsp", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, + mojo.getWebXml().toString(), null, null, null, }, new String[] { "org/web/app/last-exile.jsp" } ); + } + + public void testClassifier() + throws Exception + { + String testId = "Classifier"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + ProjectHelperStub projectHelper = new ProjectHelperStub(); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "projectHelper", projectHelper ); + setVariableValueToObject( mojo, "classifier", "test-classifier" ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple-test-classifier.war" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "WEB-INF/web.xml", "pansit.jsp", + "org/web/app/last-exile.jsp", "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, + mojo.getWebXml().toString(), null, null, null, null } ); + } + + public void testPrimaryArtifact() + throws Exception + { + String testId = "PrimaryArtifact"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + ProjectHelperStub projectHelper = new ProjectHelperStub(); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + warArtifact.setFile( new File( "error.war" ) ); + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "projectHelper", projectHelper ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple.war" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "WEB-INF/web.xml", "pansit.jsp", + "org/web/app/last-exile.jsp", "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, + mojo.getWebXml().toString(), null, null, null, null } ); + } + + public void testNotPrimaryArtifact() + throws Exception + { + // use a different pom + File pom = new File( getBasedir(), "target/test-classes/unit/warmojotest/not-primary-artifact.xml" ); + mojo = (WarMojo) lookupMojo( "war", pom ); + + String testId = "NotPrimaryArtifact"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + ProjectHelperStub projectHelper = new ProjectHelperStub(); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + warArtifact.setFile( new File( "error.war" ) ); + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "projectHelper", projectHelper ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple.war" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "WEB-INF/web.xml", "pansit.jsp", + "org/web/app/last-exile.jsp", "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, + mojo.getWebXml().toString(), null, null, null, null } ); + } + + public void testMetaInfContent() + throws Exception + { + String testId = "SimpleWarWithMetaInfContent"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + // Create the sample config.xml + final File configFile = new File( webAppSource, "META-INF/config.xml" ); + createFile( configFile, "" ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple.war" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "META-INF/config.xml", + "WEB-INF/web.xml", "pansit.jsp", "org/web/app/last-exile.jsp", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, null, + mojo.getWebXml().toString(), null, null, null, null } ); + } + + public void testMetaInfContentWithContainerConfig() + throws Exception + { + String testId = "SimpleWarWithContainerConfig"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + // Create the sample config.xml + final File configFile = new File( webAppSource, "META-INF/config.xml" ); + createFile( configFile, "" ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + mojo.setContainerConfigXML( configFile ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple.war" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "META-INF/config.xml", + "WEB-INF/web.xml", "pansit.jsp", "org/web/app/last-exile.jsp", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, null, + mojo.getWebXml().toString(), null, null, null, null } ); + } + + public void testFailOnMissingWebXmlFalse() + throws Exception + { + + String testId = "SimpleWarMissingWebXmlFalse"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setFailOnMissingWebXml( false ); + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple.war" ); + final Map jarContent = + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "pansit.jsp", + "org/web/app/last-exile.jsp", "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, new String[] { null, null, + null, null, null } ); + + assertFalse( "web.xml should be missing", jarContent.containsKey( "WEB-INF/web.xml" ) ); + } + + public void testFailOnMissingWebXmlTrue() + throws Exception + { + + String testId = "SimpleWarMissingWebXmlTrue"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setFailOnMissingWebXml( true ); + + try + { + mojo.execute(); + fail( "Building of the war isn't possible because web.xml is missing" ); + } + catch ( MojoExecutionException e ) + { + // expected behaviour + } + } + + public void testFailOnMissingWebXmlNotSpecifiedAndServlet30Used() + throws Exception + { + String testId = "SimpleWarUnderServlet30"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + + final ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE, "jar" ); + JarArtifactStub jarArtifactStub = new JarArtifactStub( getBasedir(), artifactHandler ); + jarArtifactStub.setFile( new File( getBasedir(), + "/target/test-classes/unit/sample_wars/javax.servlet-api-3.0.1.jar" ) ); + jarArtifactStub.setScope( Artifact.SCOPE_PROVIDED ); + project.addArtifact( jarArtifactStub ); + + project.setArtifact( warArtifact ); + project.setFile( warArtifact.getFile() ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + + mojo.execute(); + + // validate war file + File expectedWarFile = new File( outputDir, "simple.war" ); + final Map jarContent = + assertJarContent( expectedWarFile, + new String[] { "META-INF/MANIFEST.MF", "pansit.jsp", "org/web/app/last-exile.jsp", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.xml", + "META-INF/maven/org.apache.maven.plugin.test/maven-war-plugin-test/pom.properties" }, + new String[] { null, null, null, null, null } ); + + assertFalse( "web.xml should be missing", jarContent.containsKey( "WEB-INF/web.xml" ) ); + } + + public void testFailOnMissingWebXmlNotSpecifiedAndServlet30NotUsed() + throws Exception + { + String testId = "SimpleWarNotUnderServlet30"; + MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + + project.setArtifact( warArtifact ); + project.setFile( warArtifact.getFile() ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + + try + { + mojo.execute(); + fail( "Building of the war isn't possible because no 'failOnMissingWebXml' policy was set and the project " + + "does not depend on Servlet 3.0" ); + } + catch ( MojoExecutionException e ) + { + // expected behaviour + } + } + + public void testAttachClasses() + throws Exception + { + String testId = "AttachClasses"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, false ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + mojo.setAttachClasses( true ); + mojo.setClassesClassifier( "classes" ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple-classes.jar" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "sample-servlet.class" }, + new String[] { null, null } ); + } + + public void testAttachClassesWithCustomClassifier() + throws Exception + { + String testId = "AttachClassesCustomClassifier"; + MavenProject4CopyConstructor project = new MavenProject4CopyConstructor(); + String outputDir = getTestDirectory().getAbsolutePath() + "/" + testId + "-output"; + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifact4CCStub warArtifact = new WarArtifact4CCStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, false ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + project.setArtifact( warArtifact ); + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + mojo.setAttachClasses( true ); + mojo.setClassesClassifier( "mystuff" ); + + mojo.execute(); + + // validate jar file + File expectedJarFile = new File( outputDir, "simple-mystuff.jar" ); + assertJarContent( expectedJarFile, new String[] { "META-INF/MANIFEST.MF", "sample-servlet.class" }, + new String[] { null, null } ); + } + + protected Map assertJarContent( final File expectedJarFile, final String[] files, + final String[] filesContent ) + throws IOException + { + return assertJarContent( expectedJarFile, files, filesContent, null ); + } + + protected Map assertJarContent( final File expectedJarFile, final String[] files, + final String[] filesContent, final String[] mustNotBeInJar ) + throws IOException + { + // Sanity check + assertEquals( "Could not test, files and filesContent length does not match", files.length, filesContent.length ); + + assertTrue( "war file not created: " + expectedJarFile.toString(), expectedJarFile.exists() ); + final Map jarContent = new HashMap<>(); + try ( JarFile jarFile = new JarFile( expectedJarFile ) ) { + Enumeration enumeration = jarFile.entries(); + while ( enumeration.hasMoreElements() ) + { + JarEntry entry = enumeration.nextElement(); + Object previousValue = jarContent.put( entry.getName(), entry ); + assertNull( "Duplicate Entry in Jar File: " + entry.getName(), previousValue ); + } + + for ( int i = 0; i < files.length; i++ ) + { + String file = files[i]; + + assertTrue( "File[" + file + "] not found in archive", jarContent.containsKey( file ) ); + if ( filesContent[i] != null ) + { + assertEquals( "Content of file[" + file + "] does not match", filesContent[i], + IOUtil.toString( jarFile.getInputStream( jarContent.get( file ) ) ) ); + } + } + if ( mustNotBeInJar != null ) + { + for ( String file : mustNotBeInJar ) + { + assertFalse( "File[" + file + "] found in archive", jarContent.containsKey( file ) ); + + } + } + return jarContent; + } + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarOverlaysTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarOverlaysTest.java new file mode 100644 index 000000000..557357417 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarOverlaysTest.java @@ -0,0 +1,494 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugins.war.overlay.DefaultOverlay; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Stephane Nicoll + */ +public class WarOverlaysTest + extends AbstractWarExplodedMojoTest +{ + + private static File pomFile = new File( getBasedir(), "target/test-classes/unit/waroverlays/default.xml" ); + + public void setUp() + throws Exception + { + super.setUp(); + generateFullOverlayWar( "overlay-full-1" ); + generateFullOverlayWar( "overlay-full-2" ); + generateFullOverlayWar( "overlay-full-3" ); + } + + protected File getPomFile() + { + return pomFile; + } + + protected File getTestDirectory() + { + return new File( getBasedir(), "target/test-classes/unit/waroverlays" ); + } + + public void testEnvironment() + throws Exception + { + // see setup + } + + public void testNoOverlay() + throws Exception + { + // setup test data + final String testId = "no-overlay"; + final File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + + final File webAppDirectory = setUpMojo( testId, null ); + try + { + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + mojo.execute(); + + // Validate content of the webapp + assertDefaultContent( webAppDirectory ); + assertWebXml( webAppDirectory ); + } + finally + { + cleanDirectory( webAppDirectory ); + } + } + + public void testDefaultOverlay() + throws Exception + { + // setup test data + final String testId = "default-overlay"; + + // Add an overlay + final ArtifactStub overlay = buildWarOverlayStub( "overlay-one" ); + + final File webAppDirectory = setUpMojo( testId, new ArtifactStub[] { overlay } ); + final List assertedFiles = new ArrayList<>(); + try + { + mojo.execute(); + assertedFiles.addAll( assertDefaultContent( webAppDirectory ) ); + assertedFiles.addAll( assertWebXml( webAppDirectory ) ); + assertedFiles.addAll( assertCustomContent( webAppDirectory, new String[] { "index.jsp", "login.jsp" }, + "overlay file not found" ) ); + + // index and login come from overlay1 + assertOverlayedFile( webAppDirectory, "overlay-one", "index.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-one", "login.jsp" ); + + // Ok now check that there is no more files/directories + final FileFilter filter = new FileFilterImpl( webAppDirectory, new String[] { MANIFEST_PATH } ); + assertWebAppContent( webAppDirectory, assertedFiles, filter ); + } + finally + { + cleanDirectory( webAppDirectory ); + } + } + + public void testDefaultOverlays() + throws Exception + { + // setup test data + final String testId = "default-overlays"; + + // Add an overlay + final ArtifactStub overlay = buildWarOverlayStub( "overlay-one" ); + final ArtifactStub overlay2 = buildWarOverlayStub( "overlay-two" ); + + final File webAppDirectory = setUpMojo( testId, new ArtifactStub[] { overlay, overlay2 } ); + final List assertedFiles = new ArrayList<>(); + try + { + mojo.execute(); + assertedFiles.addAll( assertDefaultContent( webAppDirectory ) ); + assertedFiles.addAll( assertWebXml( webAppDirectory ) ); + assertedFiles.addAll( assertCustomContent( webAppDirectory, new String[] { "index.jsp", "login.jsp", + "admin.jsp" }, "overlay file not found" ) ); + + // index and login come from overlay1 + assertOverlayedFile( webAppDirectory, "overlay-one", "index.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-one", "login.jsp" ); + + // admin comes from overlay2 + // index and login comes from overlay1 + assertOverlayedFile( webAppDirectory, "overlay-two", "admin.jsp" ); + + // Ok now check that there is no more files/directories + final FileFilter filter = new FileFilterImpl( webAppDirectory, new String[] { MANIFEST_PATH } ); + assertWebAppContent( webAppDirectory, assertedFiles, filter ); + } + finally + { + cleanDirectory( webAppDirectory ); + } + } + + /** + * Merge a dependent WAR when a file in the war source directory overrides one found in the WAR. + * + * It also tests completeness of the resulting war as well as the proper order of dependencies. + * + * @throws Exception if any error occurs + */ + public void testScenarioOneWithDefaulSettings() + throws Exception + { + // setup test data + final String testId = "scenario-one-default-settings"; + + // Add an overlay + final ArtifactStub overlay1 = buildWarOverlayStub( "overlay-full-1" ); + final ArtifactStub overlay2 = buildWarOverlayStub( "overlay-full-2" ); + final ArtifactStub overlay3 = buildWarOverlayStub( "overlay-full-3" ); + + final File webAppDirectory = + setUpMojo( testId, new ArtifactStub[] { overlay1, overlay2, overlay3 }, new String[] { + "org/sample/company/test.jsp", "jsp/b.jsp" } ); + + assertScenariOne( testId, webAppDirectory ); + } + + /** + * Tests that specifying the overlay explicitely has the same behavior as the default (i.e. order, etc). + * + * The default project is not specified in this case so it is processed first by default + * + * @throws Exception if an error occurs + */ + public void testScenarioOneWithOverlaySettings() + throws Exception + { + // setup test data + final String testId = "scenario-one-overlay-settings"; + + // Add an overlay + final ArtifactStub overlay1 = buildWarOverlayStub( "overlay-full-1" ); + final ArtifactStub overlay2 = buildWarOverlayStub( "overlay-full-2" ); + final ArtifactStub overlay3 = buildWarOverlayStub( "overlay-full-3" ); + + final File webAppDirectory = + setUpMojo( testId, new ArtifactStub[] { overlay1, overlay2, overlay3 }, new String[] { + "org/sample/company/test.jsp", "jsp/b.jsp" } ); + + // Add the tags + final List overlays = new ArrayList<>(); + overlays.add( new DefaultOverlay( overlay1 ) ); + overlays.add( new DefaultOverlay( overlay2 ) ); + overlays.add( new DefaultOverlay( overlay3 ) ); + mojo.setOverlays( overlays ); + + // current project ignored. Should be on top of the list + assertScenariOne( testId, webAppDirectory ); + } + + /** + * Tests that specifying the overlay explicitely has the same behavior as the default (i.e. order, etc). + * + * The default project is explicitely specified so this should match the default. + * + * @throws Exception if an error occurs + */ + public void testScenarioOneWithFullSettings() + throws Exception + { + // setup test data + final String testId = "scenario-one-full-settings"; + + // Add an overlay + final ArtifactStub overlay1 = buildWarOverlayStub( "overlay-full-1" ); + final ArtifactStub overlay2 = buildWarOverlayStub( "overlay-full-2" ); + final ArtifactStub overlay3 = buildWarOverlayStub( "overlay-full-3" ); + + final File webAppDirectory = + setUpMojo( testId, new ArtifactStub[] { overlay1, overlay2, overlay3 }, new String[] { + "org/sample/company/test.jsp", "jsp/b.jsp" } ); + + // Add the tags + final List overlays = new ArrayList<>(); + + // Add the default project explicitely + overlays.add( mojo.getCurrentProjectOverlay() ); + + // Other overlays + overlays.add( new DefaultOverlay( overlay1 ) ); + overlays.add( new DefaultOverlay( overlay2 ) ); + overlays.add( new DefaultOverlay( overlay3 ) ); + mojo.setOverlays( overlays ); + + // current project ignored. Should be on top of the list + assertScenariOne( testId, webAppDirectory ); + } + + /** + * Runs the mojo and asserts a scenerio with 3 overlays and no includes/excludes settings. + * + * @param testId thie id of the test + * @param webAppDirectory the webapp directory + * @throws Exception if an exception occurs + */ + private void assertScenariOne( String testId, File webAppDirectory ) + throws Exception + { + final List assertedFiles = new ArrayList<>(); + try + { + mojo.execute(); + assertedFiles.addAll( assertWebXml( webAppDirectory ) ); + assertedFiles.addAll( assertCustomContent( webAppDirectory, new String[] { "jsp/a.jsp", "jsp/b.jsp", + "jsp/c.jsp", "jsp/d/a.jsp", "jsp/d/b.jsp", "jsp/d/c.jsp", "org/sample/company/test.jsp", + "WEB-INF/classes/a.class", "WEB-INF/classes/b.class", "WEB-INF/classes/c.class", "WEB-INF/lib/a.jar", + "WEB-INF/lib/b.jar", "WEB-INF/lib/c.jar" }, "overlay file not found" ) ); + + // Those files should come from the source webapp without any config + assertDefaultFileContent( testId, webAppDirectory, "jsp/b.jsp" ); + assertDefaultFileContent( testId, webAppDirectory, "org/sample/company/test.jsp" ); + + // Everything else comes from overlay1 (order of addition in the dependencies) + assertOverlayedFile( webAppDirectory, "overlay-full-1", "jsp/a.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "jsp/c.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "jsp/d/a.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "jsp/d/b.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "jsp/d/c.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "WEB-INF/web.xml" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "WEB-INF/classes/a.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "WEB-INF/classes/b.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "WEB-INF/classes/c.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "WEB-INF/lib/a.jar" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "WEB-INF/lib/b.jar" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "WEB-INF/lib/c.jar" ); + + // Ok now check that there is no more files/directories + final FileFilter filter = new FileFilterImpl( webAppDirectory, new String[] { MANIFEST_PATH } ); + assertWebAppContent( webAppDirectory, assertedFiles, filter ); + } + finally + { + cleanDirectory( webAppDirectory ); + } + } + + public void testOverlaysIncludesExcludesWithMultipleDefinitions() + throws Exception + { + // setup test data + final String testId = "overlays-includes-excludes-multiple-defs"; + + // Add an overlay + final ArtifactStub overlay1 = buildWarOverlayStub( "overlay-full-1" ); + final ArtifactStub overlay2 = buildWarOverlayStub( "overlay-full-2" ); + final ArtifactStub overlay3 = buildWarOverlayStub( "overlay-full-3" ); + + final File webAppDirectory = + setUpMojo( testId, new ArtifactStub[] { overlay1, overlay2, overlay3 }, new String[] { + "org/sample/company/test.jsp", "jsp/b.jsp" } ); + + Overlay over1 = new DefaultOverlay( overlay3 ); + over1.setExcludes( "**/a.*,**/c.*,**/*.xml" ); + + Overlay over2 = new DefaultOverlay( overlay1 ); + over2.setIncludes( "jsp/d/*" ); + over2.setExcludes( "jsp/d/a.jsp" ); + + Overlay over3 = new DefaultOverlay( overlay3 ); + over3.setIncludes( "**/*.jsp" ); + + Overlay over4 = new DefaultOverlay( overlay2 ); + + mojo.setOverlays( new LinkedList() ); + mojo.addOverlay( over1 ); + mojo.addOverlay( over2 ); + mojo.addOverlay( over3 ); + mojo.addOverlay( mojo.getCurrentProjectOverlay() ); + mojo.addOverlay( over4 ); + + final List assertedFiles = new ArrayList<>(); + try + { + mojo.execute(); + assertedFiles.addAll( assertWebXml( webAppDirectory ) ); + assertedFiles.addAll( assertCustomContent( webAppDirectory, new String[] { "jsp/a.jsp", "jsp/b.jsp", + "jsp/c.jsp", "jsp/d/a.jsp", "jsp/d/b.jsp", "jsp/d/c.jsp", "org/sample/company/test.jsp", + "WEB-INF/classes/a.class", "WEB-INF/classes/b.class", "WEB-INF/classes/c.class", "WEB-INF/lib/a.jar", + "WEB-INF/lib/b.jar", "WEB-INF/lib/c.jar" }, "overlay file not found" ) ); + + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/a.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/b.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/c.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/d/a.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/d/b.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "jsp/d/c.jsp" ); + assertDefaultFileContent( testId, webAppDirectory, "org/sample/company/test.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/web.xml" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/classes/a.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "WEB-INF/classes/b.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/classes/c.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/lib/a.jar" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "WEB-INF/lib/b.jar" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/lib/c.jar" ); + + // Ok now check that there is no more files/directories + final FileFilter filter = new FileFilterImpl( webAppDirectory, new String[] { MANIFEST_PATH } ); + assertWebAppContent( webAppDirectory, assertedFiles, filter ); + } + finally + { + cleanDirectory( webAppDirectory ); + } + } + + public void testOverlaysIncludesExcludesWithMultipleDefinitions2() + throws Exception + { + // setup test data + final String testId = "overlays-includes-excludes-multiple-defs2"; + + // Add an overlay + final ArtifactStub overlay1 = buildWarOverlayStub( "overlay-full-1" ); + final ArtifactStub overlay2 = buildWarOverlayStub( "overlay-full-2" ); + final ArtifactStub overlay3 = buildWarOverlayStub( "overlay-full-3" ); + + final File webAppDirectory = + setUpMojo( testId, new ArtifactStub[] { overlay1, overlay2, overlay3 }, new String[] { + "org/sample/company/test.jsp", "jsp/b.jsp" } ); + + Overlay over1 = new DefaultOverlay( overlay3 ); + over1.setExcludes( "**/a.*,**/c.*,**/*.xml,jsp/b.jsp" ); + + Overlay over2 = new DefaultOverlay( overlay1 ); + over2.setIncludes( "jsp/d/*" ); + over2.setExcludes( "jsp/d/a.jsp" ); + + Overlay over3 = new DefaultOverlay( overlay3 ); + over3.setIncludes( "**/*.jsp" ); + over3.setExcludes( "jsp/b.jsp" ); + + Overlay over4 = new DefaultOverlay( overlay2 ); + + mojo.setOverlays( new LinkedList() ); + mojo.addOverlay( over1 ); + mojo.addOverlay( over2 ); + mojo.addOverlay( over3 ); + mojo.addOverlay( mojo.getCurrentProjectOverlay() ); + mojo.addOverlay( over4 ); + + final List assertedFiles = new ArrayList<>(); + try + { + mojo.execute(); + assertedFiles.addAll( assertWebXml( webAppDirectory ) ); + assertedFiles.addAll( assertCustomContent( webAppDirectory, new String[] { "jsp/a.jsp", "jsp/b.jsp", + "jsp/c.jsp", "jsp/d/a.jsp", "jsp/d/b.jsp", "jsp/d/c.jsp", "org/sample/company/test.jsp", + "WEB-INF/classes/a.class", "WEB-INF/classes/b.class", "WEB-INF/classes/c.class", "WEB-INF/lib/a.jar", + "WEB-INF/lib/b.jar", "WEB-INF/lib/c.jar" }, "overlay file not found" ) ); + + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/a.jsp" ); + assertDefaultFileContent( testId, webAppDirectory, "jsp/b.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/c.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/d/a.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "jsp/d/b.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-1", "jsp/d/c.jsp" ); + assertDefaultFileContent( testId, webAppDirectory, "org/sample/company/test.jsp" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/web.xml" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/classes/a.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "WEB-INF/classes/b.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/classes/c.class" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/lib/a.jar" ); + assertOverlayedFile( webAppDirectory, "overlay-full-3", "WEB-INF/lib/b.jar" ); + assertOverlayedFile( webAppDirectory, "overlay-full-2", "WEB-INF/lib/c.jar" ); + + // Ok now check that there is no more files/directories + final FileFilter filter = new FileFilterImpl( webAppDirectory, new String[] { MANIFEST_PATH } ); + assertWebAppContent( webAppDirectory, assertedFiles, filter ); + } + finally + { + cleanDirectory( webAppDirectory ); + } + + } + + // Helpers + + /** + * Asserts that the content of an overlayed file is correct. + * + * Note that the filePath is relative to both the webapp directory and the overlayed directory, defined by + * the overlayId. + * + * @param webAppDirectory the webapp directory + * @param overlayId the id of the overlay + * @param filePath the relative path + * @throws IOException if an error occurred while reading the files + */ + protected void assertOverlayedFile( File webAppDirectory, String overlayId, String filePath ) + throws IOException + { + final File webAppFile = new File( webAppDirectory, filePath ); + final File overlayFile = getOverlayFile( overlayId, filePath ); + assertEquals( "Wrong content for overlayed file " + filePath, FileUtils.fileRead( overlayFile ), + FileUtils.fileRead( webAppFile ) ); + + } + + /** + * Asserts that the content of an overlayed file is correct. + * + * Note that the filePath is relative to both the webapp directory and the overlayed directory, defined by + * the overlayId. + * + * @param testId te id of the test + * @param webAppDirectory the webapp directory + * @param filePath the relative path + * @throws IOException if an error occurred while reading the files + */ + protected void assertDefaultFileContent( String testId, File webAppDirectory, String filePath ) + throws Exception + { + final File webAppFile = new File( webAppDirectory, filePath ); + final File sourceFile = new File( getWebAppSource( testId ), filePath ); + final String expectedContent = sourceFile.toString(); + assertEquals( "Wrong content for file " + filePath, expectedContent, FileUtils.fileRead( webAppFile ) ); + + } + + protected ArtifactStub generateSimpleWarArtifactStub( String id ) + throws Exception + { + return buildWarOverlayStub( id ); + } +} \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarZipTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarZipTest.java new file mode 100644 index 000000000..88f665533 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/WarZipTest.java @@ -0,0 +1,182 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.plugins.war.overlay.DefaultOverlay; +import org.apache.maven.plugins.war.stub.MavenZipProject; +import org.apache.maven.plugins.war.stub.WarArtifactStub; +import org.apache.maven.plugins.war.stub.ZipArtifactStub; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.util.LinkedList; + +/** + * @author Olivier Lamy + * @since 7 Oct 07 + */ +public class WarZipTest + extends AbstractWarMojoTest +{ + WarMojo mojo; + + private static File pomFile = new File( getBasedir(), "src/test/resources/unit/warziptest/war-with-zip.xml" ); + + protected File getTestDirectory() + { + return new File( getBasedir(), "target/test-classes/unit/warziptest" ); + } + + public void setUp() + throws Exception + { + super.setUp(); + mojo = (WarMojo) lookupMojo( "war", pomFile ); + } + + private Artifact buildZipArtifact() + throws Exception + { + ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE, "jar" ); + File zipFile = new File( getTestDirectory(), "foobar.zip" ); + return new ZipArtifactStub( "src/test/resources/unit/warziptest", artifactHandler, zipFile ); + } + + private File configureMojo( String testId ) + throws Exception + { + MavenZipProject project = new MavenZipProject(); + String outputDir = getTestDirectory().getAbsolutePath() + File.separatorChar + testId + "-output"; + // clean up + File outputDirFile = new File( outputDir ); + if ( outputDirFile.exists() ) + { + FileUtils.deleteDirectory( outputDirFile ); + } + File webAppDirectory = new File( getTestDirectory(), testId ); + WarArtifactStub warArtifact = new WarArtifactStub( getBasedir() ); + String warName = "simple"; + File webAppSource = createWebAppSource( testId ); + File classesDir = createClassesDir( testId, true ); + File xmlSource = createXMLConfigDir( testId, new String[] { "web.xml" } ); + project.setArtifact( warArtifact ); + + this.configureMojo( mojo, new LinkedList(), classesDir, webAppSource, webAppDirectory, project ); + setVariableValueToObject( mojo, "outputDirectory", outputDir ); + setVariableValueToObject( mojo, "warName", warName ); + setVariableValueToObject( mojo, "workDirectory", new File( getTestDirectory(), "work" ) ); + mojo.setWebXml( new File( xmlSource, "web.xml" ) ); + + project.getArtifacts().add( buildZipArtifact() ); + + return webAppDirectory; + } + + public void testOneZipWithNoSkip() + throws Exception + { + File webAppDirectory = configureMojo( "one-zip" ); + + Overlay overlay = new DefaultOverlay( buildZipArtifact() ); + // overlay.setSkip( false ); + overlay.setType( "zip" ); + mojo.addOverlay( overlay ); + mojo.execute(); + + File foo = new File( webAppDirectory, "foo.txt" ); + assertTrue( "foo.txt not exists", foo.exists() ); + assertTrue( "foo.txt not a file", foo.isFile() ); + + File barDirectory = new File( webAppDirectory, "bar" ); + assertTrue( "bar directory not exists", barDirectory.exists() ); + assertTrue( "bar not a directory", barDirectory.isDirectory() ); + + File bar = new File( barDirectory, "bar.txt" ); + assertTrue( "bar/bar.txt not exists", bar.exists() ); + assertTrue( "bar/bar.txt not a file", bar.isFile() ); + } + + public void testOneZipWithTargetPathOverlay() + throws Exception + { + File webAppDirectory = configureMojo( "one-zip-overlay-targetPath" ); + + Overlay overlay = new DefaultOverlay( buildZipArtifact() ); + overlay.setSkip( false ); + overlay.setType( "zip" ); + overlay.setTargetPath( "overridePath" ); + mojo.addOverlay( overlay ); + + mojo.execute(); + + File foo = new File( webAppDirectory.getPath() + File.separatorChar + "overridePath", "foo.txt" ); + assertTrue( "foo.txt not exists", foo.exists() ); + assertTrue( "foo.txt not a file", foo.isFile() ); + + File barDirectory = new File( webAppDirectory.getPath() + File.separatorChar + "overridePath", "bar" ); + assertTrue( "bar directory not exists", barDirectory.exists() ); + assertTrue( "bar not a directory", barDirectory.isDirectory() ); + + File bar = new File( barDirectory, "bar.txt" ); + assertTrue( "bar/bar.txt not exists", bar.exists() ); + assertTrue( "bar/bar.txt not a file", bar.isFile() ); + } + + public void testOneZipDefaultSkip() + throws Exception + { + File webAppDirectory = configureMojo( "one-zip-overlay-skip" ); + + mojo.execute(); + + assertZipContentNotHere( webAppDirectory ); + } + + public void testOneZipWithForceSkip() + throws Exception + { + File webAppDirectory = configureMojo( "one-zip-overlay-skip" ); + Overlay overlay = new DefaultOverlay( buildZipArtifact() ); + overlay.setSkip( true ); + overlay.setType( "zip" ); + mojo.addOverlay( overlay ); + + mojo.execute(); + assertZipContentNotHere( webAppDirectory ); + + } + + protected void assertZipContentNotHere( File webAppDirectory ) + { + File foo = new File( webAppDirectory.getPath() + File.separatorChar + "overridePath", "foo.txt" ); + assertFalse( "foo.txt exists", foo.exists() ); + assertFalse( "foo.txt a file", foo.isFile() ); + + File barDirectory = new File( webAppDirectory.getPath() + File.separatorChar + "overridePath", "bar" ); + assertFalse( "bar directory exists", barDirectory.exists() ); + assertFalse( "bar is a directory", barDirectory.isDirectory() ); + + File bar = new File( barDirectory, "bar.txt" ); + assertFalse( "bar/bar.txt exists", bar.exists() ); + assertFalse( "bar/bar.txt is a file", bar.isFile() ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/overlay/OverlayManagerTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/overlay/OverlayManagerTest.java new file mode 100644 index 000000000..59ed9e0c7 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/overlay/OverlayManagerTest.java @@ -0,0 +1,226 @@ +package org.apache.maven.plugins.war.overlay; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import static org.apache.maven.plugins.war.Overlay.DEFAULT_INCLUDES; +import static org.apache.maven.plugins.war.Overlay.DEFAULT_EXCLUDES; + +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub; +import org.apache.maven.plugins.war.stub.WarArtifactStub; +import org.codehaus.plexus.PlexusTestCase; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Stephane Nicoll + */ +public class OverlayManagerTest + extends PlexusTestCase +{ + + + public void testEmptyProject() + throws Exception + { + final MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + final List overlays = new ArrayList<>(); + try + { + final Overlay currentProjectOverlay = Overlay.createInstance(); + OverlayManager manager = new OverlayManager( overlays, project, DEFAULT_INCLUDES, DEFAULT_EXCLUDES, + currentProjectOverlay ); + assertNotNull( manager.getOverlays() ); + assertEquals( 1, manager.getOverlays().size() ); + assertEquals( currentProjectOverlay, manager.getOverlays().get( 0 ) ); + } + catch ( InvalidOverlayConfigurationException e ) + { + e.printStackTrace(); + fail( "Should not have failed to validate a valid overly config " + e.getMessage() ); + } + } + + public void testAutodetectSimpleOverlay( Overlay currentProjectOverlay ) + throws Exception + { + + final MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + final ArtifactStub first = newWarArtifact( "test", "test-webapp" ); + project.addArtifact( first ); + + final List overlays = new ArrayList<>(); + + try + { + final Overlay overlay = currentProjectOverlay; + OverlayManager manager = new OverlayManager( overlays, project, DEFAULT_INCLUDES, DEFAULT_EXCLUDES, + overlay ); + assertNotNull( manager.getOverlays() ); + assertEquals( 2, manager.getOverlays().size() ); + assertEquals( overlay, manager.getOverlays().get( 0 ) ); + assertEquals( new DefaultOverlay( first ), manager.getOverlays().get( 1 ) ); + } + catch ( InvalidOverlayConfigurationException e ) + { + e.printStackTrace(); + fail( "Should not have failed to validate a valid overlay config " + e.getMessage() ); + } + } + + public void testSimpleOverlay() + throws Exception + { + + final MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + final ArtifactStub first = newWarArtifact( "test", "test-webapp" ); + project.addArtifact( first ); + + final List overlays = new ArrayList<>(); + overlays.add( new DefaultOverlay( first ) ); + + try + { + final Overlay currentProjectOverlay = Overlay.createInstance(); + OverlayManager manager = new OverlayManager( overlays, project, DEFAULT_INCLUDES, DEFAULT_EXCLUDES, + currentProjectOverlay ); + assertNotNull( manager.getOverlays() ); + assertEquals( 2, manager.getOverlays().size() ); + assertEquals( Overlay.createInstance(), manager.getOverlays().get( 0 ) ); + assertEquals( overlays.get( 0 ), manager.getOverlays().get( 1 ) ); + } + catch ( InvalidOverlayConfigurationException e ) + { + e.printStackTrace(); + fail( "Should not have failed to validate a valid overlay config " + e.getMessage() ); + } + } + + public void testUnknownOverlay() + throws Exception + { + + final MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + final ArtifactStub first = newWarArtifact( "test", "test-webapp" ); + project.addArtifact( first ); + + final List overlays = new ArrayList<>(); + overlays.add( new Overlay( "test", "test-webapp-2" ) ); + + try + { + final Overlay currentProjectOverlay = Overlay.createInstance(); + new OverlayManager( overlays, project, DEFAULT_INCLUDES, DEFAULT_EXCLUDES, currentProjectOverlay ); + fail( "Should have failed to validate an unknown overlay" ); + } + catch ( InvalidOverlayConfigurationException e ) + { + // OK + } + } + + public void testCustomCurrentProject() + throws Exception + { + + final MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + final ArtifactStub first = newWarArtifact( "test", "test-webapp" ); + final ArtifactStub second = newWarArtifact( "test", "test-webapp-2" ); + project.addArtifact( first ); + project.addArtifact( second ); + + final List overlays = new ArrayList<>(); + overlays.add( new DefaultOverlay( first ) ); + final Overlay currentProjectOverlay = Overlay.createInstance(); + overlays.add( currentProjectOverlay ); + + try + { + OverlayManager manager = new OverlayManager( overlays, project, DEFAULT_INCLUDES, DEFAULT_EXCLUDES, + currentProjectOverlay ); + assertNotNull( manager.getOverlays() ); + assertEquals( 3, manager.getOverlays().size() ); + assertEquals( overlays.get( 0 ), manager.getOverlays().get( 0 ) ); + assertEquals( currentProjectOverlay, manager.getOverlays().get( 1 ) ); + assertEquals( new DefaultOverlay( second ), manager.getOverlays().get( 2 ) ); + + } + catch ( InvalidOverlayConfigurationException e ) + { + e.printStackTrace(); + fail( "Should not have failed to validate a valid overlay config " + e.getMessage() ); + } + } + + public void testOverlaysWithSameArtifactAndGroupId() + throws Exception + { + + final MavenProjectArtifactsStub project = new MavenProjectArtifactsStub(); + final ArtifactStub first = newWarArtifact( "test", "test-webapp" ); + final ArtifactStub second = newWarArtifact( "test", "test-webapp", "my-classifier" ); + + project.addArtifact( first ); + project.addArtifact( second ); + + final List overlays = new ArrayList<>(); + overlays.add( new DefaultOverlay( first ) ); + overlays.add( new DefaultOverlay( second ) ); + + try + { + final Overlay currentProjectOverlay = Overlay.createInstance(); + OverlayManager manager = new OverlayManager( overlays, project, DEFAULT_INCLUDES, DEFAULT_EXCLUDES, + currentProjectOverlay ); + assertNotNull( manager.getOverlays() ); + assertEquals( 3, manager.getOverlays().size() ); + assertEquals( currentProjectOverlay, manager.getOverlays().get( 0 ) ); + assertEquals( overlays.get( 0 ), manager.getOverlays().get( 1 ) ); + assertEquals( overlays.get( 1 ), manager.getOverlays().get( 2 ) ); + + } + catch ( InvalidOverlayConfigurationException e ) + { + e.printStackTrace(); + fail( "Should not have failed to validate a valid overlay config " + e.getMessage() ); + } + } + + + protected ArtifactStub newWarArtifact( String groupId, String artifactId, String classifier ) + { + final WarArtifactStub a = new WarArtifactStub( getBasedir() ); + a.setGroupId( groupId ); + a.setArtifactId( artifactId ); + if ( classifier != null ) + { + a.setClassifier( classifier ); + } + return a; + } + + protected ArtifactStub newWarArtifact( String groupId, String artifactId ) + { + return newWarArtifact( groupId, artifactId, null ); + + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AarArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AarArtifactStub.java new file mode 100644 index 000000000..db5004964 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AarArtifactStub.java @@ -0,0 +1,79 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +import org.apache.maven.artifact.handler.ArtifactHandler; + +import java.io.File; + +/** + * @author Stephane Nicoll + */ +public class AarArtifactStub + extends AbstractArtifactStub +{ + protected String groupId; + + private ArtifactHandler artifactHandler; + + public AarArtifactStub( String basedir, ArtifactHandler artifactHandler ) + { + super( basedir ); + this.artifactHandler = artifactHandler; + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.aar"; + } + } + + public String getType() + { + return "aar"; + } + + public String getArtifactId() + { + return "aarartifact"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.aar" ); + } + + public ArtifactHandler getArtifactHandler() + { + return artifactHandler; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java new file mode 100644 index 000000000..de6890ead --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java @@ -0,0 +1,166 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; + +public abstract class AbstractArtifactStub + extends ArtifactStub +{ + protected String basedir; + + public AbstractArtifactStub( String _basedir ) + { + basedir = _basedir; + } + + public String getVersion() + { + return "0.0-Test"; + } + + @Override + public String getBaseVersion() + { + return getVersion(); + } + + public String getScope() + { + return Artifact.SCOPE_RUNTIME; + } + + public VersionRange getVersionRange() + { + return VersionRange.createFromVersion( getVersion()); + } + + public boolean isOptional() + { + return false; + } + + public ArtifactHandler getArtifactHandler() + { + return new DefaultArtifactHandler( getType() ); + } + + /* + * TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... + */ + public int compareTo( Artifact a ) + { + /* -- We need to support groupId=null (it is missing in DefaultArtifact.java) */ + int result; + if ( a.getGroupId() != null ) + { + result = getGroupId().compareTo( a.getGroupId() ); + } + else + { + result = ( getGroupId() == null ? 0 : -1 ); + } + /* -- */ + + if ( result == 0 ) + { + result = getArtifactId().compareTo( a.getArtifactId() ); + if ( result == 0 ) + { + result = getType().compareTo( a.getType() ); + if ( result == 0 ) + { + if ( getClassifier() == null ) + { + if ( a.getClassifier() != null ) + { + result = 1; + } + } + else + { + if ( a.getClassifier() != null ) + { + result = getClassifier().compareTo( a.getClassifier() ); + } + else + { + result = -1; + } + } + if ( result == 0 ) + { + // We don't consider the version range in the comparison, just the resolved version + result = getVersion().compareTo( a.getVersion() ); + } + } + } + } + return result; + } + + /* + * TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... + */ + public boolean equals( Object o ) + { + if ( o == this ) + { + return true; + } + + if ( !( o instanceof Artifact ) ) + { + return false; + } + + Artifact a = (Artifact) o; + + /* -- We need to support groupId=null (it is missing in DefaultArtifact.java) */ + if ( a.getGroupId() == null ? ( getGroupId() != null ) : a.getGroupId().equals( getGroupId() ) ) + { + return false; + } + else if ( !a.getArtifactId().equals( getArtifactId() ) ) + { + return false; + } + else if ( !a.getVersion().equals( getVersion() ) ) + { + return false; + } + else if ( !a.getType().equals( getType() ) ) + { + return false; + } + else if ( a.getClassifier() == null ? getClassifier() != null : !a.getClassifier().equals( getClassifier() ) ) + { + return false; + } + + // We don't consider the version range in the comparison, just the resolved version + + return true; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStub.java new file mode 100644 index 000000000..7383715bd --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStub.java @@ -0,0 +1,79 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; + +import java.io.File; + +public class EJBArtifactStub + extends AbstractArtifactStub +{ + protected String groupId; + + public EJBArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.ejb"; + } + } + + public String getType() + { + return "ejb"; + } + + public String getArtifactId() + { + return "ejbartifact"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/ejb.jar" ); + } + + public ArtifactHandler getArtifactHandler() + { + return new DefaultArtifactHandler() + { + public String getExtension() + { + return "jar"; + } + }; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStubWithClassifier.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStubWithClassifier.java new file mode 100644 index 000000000..35869c5e2 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBArtifactStubWithClassifier.java @@ -0,0 +1,90 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; + +import java.io.File; + +public class EJBArtifactStubWithClassifier + extends AbstractArtifactStub +{ + protected String groupId; + protected String classifier; + + public EJBArtifactStubWithClassifier( String _basedir ) + { + super( _basedir ); + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.ejb"; + } + } + + public String getType() + { + return "ejb"; + } + + public String getArtifactId() + { + return "ejbartifact"; + } + + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + public String getClassifier() + { + return classifier; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/ejb.jar" ); + } + + public ArtifactHandler getArtifactHandler() + { + return new DefaultArtifactHandler() + { + public String getExtension() + { + return "jar"; + } + }; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBClientArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBClientArtifactStub.java new file mode 100644 index 000000000..7e978967b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/EJBClientArtifactStub.java @@ -0,0 +1,84 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; + +import java.io.File; + +public class EJBClientArtifactStub + extends AbstractArtifactStub +{ + protected String groupId; + + public EJBClientArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.ejb"; + } + } + + public String getType() + { + return "ejb-client"; + } + + public String getClassifier() + { + return "client"; + } + + public String getArtifactId() + { + return "ejbclientartifact"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/ejbclient.jar" ); + } + + public ArtifactHandler getArtifactHandler() + { + return new DefaultArtifactHandler() + { + public String getExtension() + { + return "jar"; + } + }; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/IncludeExcludeWarArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/IncludeExcludeWarArtifactStub.java new file mode 100644 index 000000000..9f879b3c4 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/IncludeExcludeWarArtifactStub.java @@ -0,0 +1,42 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +public class IncludeExcludeWarArtifactStub + extends WarArtifactStub +{ + public IncludeExcludeWarArtifactStub( String id ) + { + super( id ); + setGroupId( "wartests" ); + } + + public String getArtifactId() + { + return "war-include-exclude"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/include-exclude.war" ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java new file mode 100644 index 000000000..4b0899516 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java @@ -0,0 +1,151 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; + +import java.io.File; + +public class JarArtifactStub + extends AbstractArtifactStub +{ + + protected String groupId; + + protected String artifactId; + + protected String version; + + protected boolean optional = false; + + protected String scope; + + private File file; + + private ArtifactHandler artifactHandler; + + public JarArtifactStub( String basedir, ArtifactHandler artifactHandler ) + { + super( basedir ); + this.artifactHandler = artifactHandler; + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.jar"; + } + } + + public String getType() + { + return "jar"; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + + public String getArtifactId() + { + if ( artifactId != null ) + { + return artifactId; + } + else + { + return "jarartifact"; + } + } + + public String getVersion() + { + if ( version != null ) + { + return version; + } + else + { + return super.getVersion(); + } + } + + public void setVersion( String version ) + { + this.version = version; + } + + public boolean isOptional() + { + return optional; + } + + public void setOptional( boolean optional ) + { + this.optional = optional; + } + + public String getScope() + { + if ( scope != null ) + { + return scope; + } + else + { + return super.getScope(); + } + } + + public void setScope( String scope ) + { + this.scope = scope; + } + + public File getFile() + { + if ( file == null ) + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.jar" ); + } + return file; + } + + public void setFile( File file ) + { + this.file = file; + } + + public ArtifactHandler getArtifactHandler() + { + return artifactHandler; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MarArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MarArtifactStub.java new file mode 100644 index 000000000..e26655749 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MarArtifactStub.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.maven.plugins.war.stub; + +import org.apache.maven.artifact.handler.ArtifactHandler; + +import java.io.File; + +/** + * @author Stephane Nicoll + */ +public class MarArtifactStub + extends AbstractArtifactStub +{ + protected String groupId; + + private ArtifactHandler artifactHandler; + + public MarArtifactStub( String basedir, ArtifactHandler artifactHandler ) + { + super( basedir ); + this.artifactHandler = artifactHandler; + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.mar"; + } + } + + public String getType() + { + return "mar"; + } + + public String getArtifactId() + { + return "marartifact"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.mar" ); + } + + public ArtifactHandler getArtifactHandler() + { + return artifactHandler; + } +} + diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProject4CopyConstructor.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProject4CopyConstructor.java new file mode 100644 index 000000000..e9a58ab12 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProject4CopyConstructor.java @@ -0,0 +1,64 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.model.Profile; +import org.apache.maven.project.MavenProject; + +public class MavenProject4CopyConstructor + extends MavenProjectBasicStub +{ + protected ModelStub model; + + public MavenProject4CopyConstructor() + throws Exception + { + initializeParentFields(); + } + + public List getAttachedArtifacts() + { + return new LinkedList<>(); + } + + // to prevent the MavenProject copy constructor from blowing up + private void initializeParentFields() + { + // the pom should be located in the isolated dummy root + super.setFile( new File( getBasedir(), "pom.xml" ) ); + super.setDependencyArtifacts( new HashSet() ); + super.setArtifacts( new HashSet() ); + super.setExtensionArtifacts( new HashSet() ); + super.setRemoteArtifactRepositories( new LinkedList() ); + super.setPluginArtifactRepositories( new LinkedList() ); + super.setCollectedProjects( new LinkedList() ); + super.setActiveProfiles( new LinkedList() ); + super.setOriginalModel( null ); + super.setExecutionProject( this ); + super.setBuild( getBuild() ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectArtifactsStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectArtifactsStub.java new file mode 100644 index 000000000..a8e21192c --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectArtifactsStub.java @@ -0,0 +1,85 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Dependency; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +public class MavenProjectArtifactsStub + extends MavenProjectBasicStub +{ + TreeSet artifacts; + + public MavenProjectArtifactsStub() + throws Exception + { + artifacts = new TreeSet<>(); + } + + public void addArtifact( ArtifactStub stub ) + { + artifacts.add( stub ); + } + + public Set getArtifacts() + { + return artifacts; + } + + public List getDependencies() + { + if ( getArtifacts() == null ) + { + return new ArrayList<>(); + } + final List dependencies = new ArrayList<>(); + for ( Artifact a : getArtifacts() ) + { + Dependency dependency = new Dependency(); + dependency.setArtifactId( a.getArtifactId() ); + dependency.setGroupId( a.getGroupId() ); + dependency.setVersion( a.getVersion() ); + dependency.setScope( a.getScope() ); + dependency.setType( a.getType() ); + dependency.setClassifier( a.getClassifier() ); + dependencies.add( dependency ); + + } + return dependencies; + } + + public List getRuntimeClasspathElements() + { + List artifacts = new ArrayList<>(); + + artifacts.add( + "src/test/resources/unit/manifest/manifest-with-classpath/sample-artifacts/maven-artifact1-1.0-SNAPSHOT.jar" ); + artifacts.add( + "src/test/resources/unit/manifest/manifest-with-classpath/sample-artifacts/maven-artifact2-1.0-SNAPSHOT.jar" ); + + return artifacts; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectBasicStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectBasicStub.java new file mode 100644 index 000000000..7b2ac3007 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenProjectBasicStub.java @@ -0,0 +1,130 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Build; +import org.apache.maven.model.Organization; +import org.apache.maven.project.MavenProject; + +/** + * Stub + */ +public class MavenProjectBasicStub + extends MavenProject +{ + protected String testRootDir; + + protected Properties properties; + + public MavenProjectBasicStub() + throws Exception + { + super( new ModelStub() ); + properties = new Properties(); + } + + public Set getArtifacts() + { + return new HashSet<>(); + } + + public String getName() + { + return "Test Project "; + } + + public File getBasedir() + { + // create an isolated environment + // see setupTestEnvironment for details + //return new File( testRootDir ); + return null; + } + + public String getGroupId() + { + return "org.apache.maven.plugin.test"; + } + + public String getArtifactId() + { + return "maven-war-plugin-test"; + } + + public String getPackaging() + { + return "jar"; + } + + public String getVersion() + { + return "0.0-Test"; + } + + public void addProperty( String key, String value ) + { + properties.put( key, value ); + } + + public Properties getProperties() + { + return properties; + } + + public String getDescription() + { + return "Test Description"; + } + + public Organization getOrganization() + { + return new Organization() + { + public String getName() + { + return "Test Name"; + } + }; + } + + @Override + public Build getBuild() + { + Build build = super.getBuild(); + + build.setDirectory( System.getProperty( "project.build.directory" ) ); + build.setOutputDirectory( System.getProperty( "project.build.outputDirectory" ) ); + + return build; + } + + @Override + public MavenProject clone() + { + return this; + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenZipProject.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenZipProject.java new file mode 100644 index 000000000..c2ff17006 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/MavenZipProject.java @@ -0,0 +1,58 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.HashSet; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; + +/** + * @author Olivier Lamy + * @since 9 juin 07 + */ +public class MavenZipProject + extends MavenProject4CopyConstructor +{ + private Set artifacts; + + public MavenZipProject() + throws Exception + { + super(); + this.artifacts = new HashSet<>(); + } + + public Set getArtifacts() + { + return this.artifacts; + } + + public Set getDependencyArtifacts() + { + return this.artifacts; + } + + public void addArtifact( Artifact artifact ) + { + this.artifacts.add( artifact ); + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ModelStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ModelStub.java new file mode 100644 index 000000000..e15b8b2c9 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ModelStub.java @@ -0,0 +1,100 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; +import org.apache.maven.model.Profile; + +/** + * Stub + */ +public class ModelStub + extends Model +{ + /** + * + */ + private static final long serialVersionUID = 7802402157311376304L; + + public ModelStub() + { + + } + + public String getVersion() + { + return "0.0-TEST"; + } + + public String getModelVersion() + { + return "0.0-TEST"; + } + + public String getName() + { + return "Test Model"; + } + + public String getGroupId() + { + return "org.apache.maven.test"; + } + + public String getPackaging() + { + return "jar"; + } + + public Parent getParent() + { + return new Parent(); + } + + public String getArtifactId() + { + return "maven-test-plugin"; + } + + public Properties getProperties() + { + return new Properties(); + } + + public List getPackages() + { + return new LinkedList(); + } + + public List getProfiles() + { + return new LinkedList<>(); + } + + public List getModules() + { + return new LinkedList<>(); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/PARArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/PARArtifactStub.java new file mode 100644 index 000000000..588b22e26 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/PARArtifactStub.java @@ -0,0 +1,46 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +public class PARArtifactStub + extends AbstractArtifactStub +{ + public PARArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public String getType() + { + return "par"; + } + + public String getArtifactId() + { + return "parartifact"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/sample.par" ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ProjectHelperStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ProjectHelperStub.java new file mode 100644 index 000000000..9fe114f99 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ProjectHelperStub.java @@ -0,0 +1,81 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.util.List; + +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.MavenProjectHelper; + +public class ProjectHelperStub + implements MavenProjectHelper +{ + File artifactFile; + + String artifactType; + + String artifactClassifier; + + public File getArtifactFile() + { + return artifactFile; + } + + public String getArtifactType() + { + return artifactType; + } + + public String getArtifactClassifier() + { + return artifactClassifier; + } + + public void attachArtifact( MavenProject project, File artifactFile, String artifactClassifier ) + { + + } + + public void attachArtifact( MavenProject project, String artifactType, File artifactFile ) + { + + } + + public void attachArtifact( MavenProject project, String _artifactType, String _artifactClassifier, + File _artifactFile ) + { + artifactType = _artifactType; + artifactClassifier = _artifactClassifier; + artifactFile = _artifactFile; + } + + @SuppressWarnings( "rawtypes" ) + public void addResource( MavenProject project, String resourceDirectory, List includes, List excludes ) + { + + } + + @SuppressWarnings( "rawtypes" ) + public void addTestResource( MavenProject project, String resourceDirectory, List includes, List excludes ) + { + + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ResourceStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ResourceStub.java new file mode 100644 index 000000000..dbe22b0b5 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ResourceStub.java @@ -0,0 +1,57 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.model.Resource; + +public class ResourceStub + extends Resource +{ + /** + * + */ + private static final long serialVersionUID = 7685068931840967662L; + + private String directory; + + public List getIncludes() + { + return new ArrayList<>(); + } + + public List getExcludes() + { + return new ArrayList<>(); + } + + public void setDirectory( String _directory ) + { + directory = _directory; + } + + public String getDirectory() + { + return directory; + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/TLDArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/TLDArtifactStub.java new file mode 100644 index 000000000..bd2c2fd6d --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/TLDArtifactStub.java @@ -0,0 +1,46 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +public class TLDArtifactStub + extends AbstractArtifactStub +{ + public TLDArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public String getType() + { + return "tld"; + } + + public String getArtifactId() + { + return "tldartifact"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/tld.jar" ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifact4CCStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifact4CCStub.java new file mode 100644 index 000000000..c2b05b29e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifact4CCStub.java @@ -0,0 +1,57 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.artifact.versioning.VersionRange; + +/** + * stub for copy constructor + * to prevent the copy constructor from blowing up + */ +public class WarArtifact4CCStub + extends WarArtifactStub +{ + public WarArtifact4CCStub( String basedir ) + { + super( basedir ); + } + + public VersionRange getVersionRange() + { + return VersionRange.createFromVersion( getVersion() ); + } + + public String getGroupId() + { + return "org.maven.plugin.test"; + } + + public String getClassifier() + { + return "testclassifier"; + } + + public ArtifactHandler getArtifactHandler() + { + return new DefaultArtifactHandler(); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java new file mode 100644 index 000000000..9b4c63ead --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java @@ -0,0 +1,115 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +/** + * Stub + */ +public class WarArtifactStub + extends AbstractArtifactStub +{ + + private String groupId; + + private String artifactId; + + private String classifier; + + private File file; + + public WarArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public String getType() + { + return "war"; + } + + public String getArtifactId() + { + if ( artifactId == null ) + { + return "simple"; + } + else + { + return artifactId; + } + } + + public void setArtifactId( String _artifactId ) + { + artifactId = _artifactId; + } + + + public String getGroupId() + { + if ( groupId == null ) + { + return "wartests"; + } + else + { + return groupId; + } + } + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + public File getFile() + { + if ( file == null ) + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.war" ); + } + else + { + return file; + } + } + + public void setFile( File _file ) + { + file = _file; + } + + public String getClassifier() + { + return classifier; + } + + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + public boolean hasClassifier() + { + return classifier != null; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarOverlayStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarOverlayStub.java new file mode 100644 index 000000000..892f9791e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/WarOverlayStub.java @@ -0,0 +1,76 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +/** + * @author Stephane Nicoll + */ +public class WarOverlayStub + extends AbstractArtifactStub +{ + + + private final String artifactId; + + private File file; + + public WarOverlayStub( String _basedir, String artifactId, File warFile ) + { + super( _basedir ); + if ( artifactId == null ) + { + throw new NullPointerException( "Id could not be null." ); + } + if ( warFile == null ) + { + throw new NullPointerException( "warFile could not be null." ); + + } + else if ( !warFile.exists() ) + { + throw new IllegalStateException( "warFile[" + file.getAbsolutePath() + "] should exist." ); + } + this.artifactId = artifactId; + this.file = warFile; + } + + public String getType() + { + return "war"; + } + + public String getArtifactId() + { + return artifactId; + } + + public String getGroupId() + { + return "wartests"; + } + + public File getFile() + { + return file; + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/XarArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/XarArtifactStub.java new file mode 100644 index 000000000..92e195f28 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/XarArtifactStub.java @@ -0,0 +1,79 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +import org.apache.maven.artifact.handler.ArtifactHandler; + +import java.io.File; + +/** + * @author Auke Schrijnen + */ +public class XarArtifactStub + extends AbstractArtifactStub +{ + protected String groupId; + + private ArtifactHandler artifactHandler; + + public XarArtifactStub( String basedir, ArtifactHandler artifactHandler ) + { + super( basedir ); + this.artifactHandler = artifactHandler; + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.xar"; + } + } + + public String getType() + { + return "xar"; + } + + public String getArtifactId() + { + return "xarartifact"; + } + + public File getFile() + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.xar" ); + } + + public ArtifactHandler getArtifactHandler() + { + return artifactHandler; + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ZipArtifactStub.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ZipArtifactStub.java new file mode 100644 index 000000000..50c01d98a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/stub/ZipArtifactStub.java @@ -0,0 +1,89 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +import org.apache.maven.artifact.handler.ArtifactHandler; + +/** + * @author Olivier Lamy + * @since 8 juin 07 + */ +public class ZipArtifactStub + extends AbstractArtifactStub +{ + private File zip; + + public ZipArtifactStub( String basedir, ArtifactHandler artifactHandler, File zipFile ) + { + super( basedir ); + super.setArtifactHandler( artifactHandler ); + this.zip = zipFile; + } + + + public String getId() + { + return null; + } + + public ArtifactHandler getArtifactHandler() + { + return super.getArtifactHandler(); + } + + public String getScope() + { + return super.getScope(); + } + + public String getVersion() + { + return "1.0"; + } + + public boolean isOptional() + { + return super.isOptional(); + } + + public File getFile() + { + return this.zip; + } + + public String getType() + { + return "zip"; + } + + + public String getArtifactId() + { + return "zipId"; + } + + public String getGroupId() + { + return "zipGroupId"; + } + +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/PathSetTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/PathSetTest.java new file mode 100644 index 000000000..6375109ef --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/PathSetTest.java @@ -0,0 +1,257 @@ +package org.apache.maven.plugins.war.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import junit.framework.TestCase; + +import org.apache.maven.plugins.war.util.PathSet; +import org.codehaus.plexus.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public class PathSetTest + extends TestCase +{ + + /* --------------- Normalization tests --------------*/ + + /** + * Test method for 'org.apache.maven.plugin.war.PathSet.normalizeSubPath(String)' + */ + public void testNormalizeSubPath() + { + assertEquals( "Normalized path error", "", PathSet.normalizeSubPath( "" ) ); + assertEquals( "Normalized path error", "", PathSet.normalizeSubPath( "/" ) ); + assertEquals( "Normalized path error", "", PathSet.normalizeSubPath( "////" ) ); + assertEquals( "Normalized path error", "", PathSet.normalizeSubPath( "\\" ) ); + assertEquals( "Normalized path error", "", PathSet.normalizeSubPath( "\\\\\\\\" ) ); + + assertEquals( "Normalized path error", "abc", PathSet.normalizeSubPath( "abc" ) ); + assertEquals( "Normalized path error", "abc", PathSet.normalizeSubPath( "/abc" ) ); + assertEquals( "Normalized path error", "abc", PathSet.normalizeSubPath( "////abc" ) ); + assertEquals( "Normalized path error", "abc", PathSet.normalizeSubPath( "\\abc" ) ); + assertEquals( "Normalized path error", "abc", PathSet.normalizeSubPath( "\\\\\\\\abc" ) ); + + assertEquals( "Normalized path error", "abc/def/xyz", PathSet.normalizeSubPath( "abc/def\\xyz\\" ) ); + assertEquals( "Normalized path error", "abc/def/xyz", PathSet.normalizeSubPath( "/abc/def/xyz/" ) ); + assertEquals( "Normalized path error", "abc/def/xyz", PathSet.normalizeSubPath( "////abc/def/xyz/" ) ); + assertEquals( "Normalized path error", "abc/def/xyz", PathSet.normalizeSubPath( "\\abc/def/xyz/" ) ); + assertEquals( "Normalized path error", "abc/def/xyz", + PathSet.normalizeSubPath( "\\\\\\\\abc/def/xyz/" ) ); + // MWAR-371 + assertEquals( "Normalized path error", "abc/def/ghi", + PathSet.normalizeSubPath( "///abc/////def////ghi//" ) ); + } + + /* -------------- Operations tests ------------------*/ + + /** + * Test method for: + *
    + *
  • org.apache.maven.plugin.war.PathSet.PathSet()
  • + *
  • org.apache.maven.plugin.war.PathSet.size()
  • + *
  • org.apache.maven.plugin.war.PathSet.add()
  • + *
  • org.apache.maven.plugin.war.PathSet.addAll()
  • + *
  • org.apache.maven.plugin.war.PathSet.iterate()
  • + *
  • org.apache.maven.plugin.war.PathSet.contains()
  • + *
  • org.apache.maven.plugin.war.PathSet.addPrefix(String)
  • + *
+ */ + public void testPathsSetBasic() + { + PathSet ps = new PathSet(); + assertEquals( "Unexpected PathSet size", ps.size(), 0 ); + Iterator iter = ps.iterator(); + assertNotNull( "Iterator is null", iter ); + assertFalse( "Can iterate on empty set", iter.hasNext() ); + + ps.add( "abc" ); + assertEquals( "Unexpected PathSet size", ps.size(), 1 ); + ps.add( "abc" ); + assertEquals( "Unexpected PathSet size", ps.size(), 1 ); + ps.add( "xyz/abc" ); + assertEquals( "Unexpected PathSet size", ps.size(), 2 ); + ps.add( "///abc" ); + assertEquals( "Unexpected PathSet size", ps.size(), 2 ); + ps.add( "///xyz\\abc" ); + assertEquals( "Unexpected PathSet size", ps.size(), 2 ); + + ps.addAll( ps ); + assertEquals( "Unexpected PathSet size", ps.size(), 2 ); + + int i = 0; + for (String pathstr : ps) { + i++; + assertTrue(ps.contains(pathstr)); + assertTrue(ps.contains("/" + pathstr)); + assertTrue(ps.contains("/" + StringUtils.replace(pathstr, '/', '\\'))); + assertFalse(ps.contains("/" + StringUtils.replace(pathstr, '/', '\\') + "/a")); + assertFalse(ps.contains("/a/" + StringUtils.replace(pathstr, '/', '\\'))); + } + assertEquals( "Wrong count of iterations", 2, i ); + + ps.addPrefix( "/ab/c/" ); + i = 0; + for (String pathstr : ps) { + i++; + assertTrue(pathstr.startsWith("ab/c/")); + assertFalse(pathstr.startsWith("ab/c//")); + assertTrue(ps.contains(pathstr)); + assertTrue(ps.contains("/" + pathstr)); + assertTrue(ps.contains("/" + StringUtils.replace(pathstr, '/', '\\'))); + assertFalse(ps.contains("/" + StringUtils.replace(pathstr, '/', '\\') + "/a")); + assertFalse(ps.contains("/ab/" + StringUtils.replace(pathstr, '/', '\\'))); + } + assertEquals( "Wrong count of iterations", 2, i ); + } + + /** + * Test method for: + *
    + *
  • org.apache.maven.plugin.war.PathSet.PathSet(Collection)
  • + *
  • org.apache.maven.plugin.war.PathSet.PathSet(String[])
  • + *
  • org.apache.maven.plugin.war.PathSet.Add
  • + *
  • org.apache.maven.plugin.war.PathSet.AddAll(String[],String)
  • + *
  • org.apache.maven.plugin.war.PathSet.AddAll(Collection,String)
  • + *
+ */ + public void testPathsSetAddAlls() + { + Set s1set = new HashSet<>(); + s1set.add( "/a/b" ); + s1set.add( "a/b/c" ); + s1set.add( "a\\b/c" ); + s1set.add( "//1//2\3a" ); + + String[] s2ar = new String[]{"/a/b", "a2/b2/c2", "a2\\b2/c2", "//21//22\23a"}; + + PathSet ps1 = new PathSet( s1set ); + assertEquals( "Unexpected PathSet size", 3, ps1.size() ); + + PathSet ps2 = new PathSet( s2ar ); + assertEquals( "Unexpected PathSet size", 3, ps2.size() ); + + ps1.addAll( s2ar ); + assertEquals( "Unexpected PathSet size", 5, ps1.size() ); + + ps2.addAll( s1set ); + assertEquals( "Unexpected PathSet size", 5, ps2.size() ); + + for (String str : ps1) { + assertTrue(str, ps2.contains(str)); + assertTrue(ps2.contains("/" + str)); + assertTrue(ps1.contains(str)); + assertTrue(ps1.contains("/" + str)); + } + + for (String str : ps2) { + assertTrue(ps1.contains(str)); + assertTrue(ps1.contains("/" + str)); + assertTrue(ps2.contains(str)); + assertTrue(ps2.contains("/" + str)); + } + + ps1.addAll( s2ar, "/pref/" ); + assertEquals( "Unexpected PathSet size", 8, ps1.size() ); + + ps2.addAll( s2ar, "/pref/" ); + assertEquals( "Unexpected PathSet size", 8, ps2.size() ); + + for (String str : ps1) { + assertTrue(str, ps2.contains(str)); + assertTrue(ps2.contains("/" + str)); + assertTrue(ps1.contains(str)); + assertTrue(ps1.contains("/" + str)); + } + + for (String str : ps2) { + assertTrue(ps1.contains(str)); + assertTrue(ps1.contains("/" + str)); + assertTrue(ps2.contains(str)); + assertTrue(ps2.contains("/" + str)); + } + + PathSet ps3 = new PathSet(); + ps3.addAll(new String[]{ "a/b/c" }, "d"); + assertTrue( "Unexpected PathSet path", ps3.contains( "d/a/b/c" ) ); + } + + /** + * Test method for 'org.apache.maven.plugin.war.PathSet.addAllFilesInDirectory(File, String)' + * + * @throws IOException if an io error occurred + */ + public void testAddAllFilesInDirectory() + throws IOException + { + PathSet ps = new PathSet(); + + /* Preparing directory structure*/ + File testDir = new File( "target/testAddAllFilesInDirectory" ); + testDir.mkdirs(); + + File f1 = new File( testDir, "f1" ); + f1.createNewFile(); + File f2 = new File( testDir, "f2" ); + f2.createNewFile(); + + File d1 = new File( testDir, "d1" ); + File d1d2 = new File( testDir, "d1/d2" ); + d1d2.mkdirs(); + File d1d2f1 = new File( d1d2, "f1" ); + d1d2f1.createNewFile(); + File d1d2f2 = new File( d1d2, "f2" ); + d1d2f2.createNewFile(); + + ps.addAllFilesInDirectory( new File( "target/testAddAllFilesInDirectory" ), "123/" ); + assertEquals( "Unexpected PathSet size", 4, ps.size() ); + + /*No changes after adding duplicates*/ + ps.addAllFilesInDirectory( new File( "target/testAddAllFilesInDirectory" ), "123/" ); + assertEquals( "Unexpected PathSet size", 4, ps.size() ); + + /*Cleanup*/ + + f1.delete(); + f2.delete(); + + /*No changes after adding a subset of files*/ + ps.addAllFilesInDirectory( new File( "target/testAddAllFilesInDirectory" ), "123/" ); + assertEquals( "Unexpected PathSet size", 4, ps.size() ); + + d1d2f1.delete(); + d1d2f2.delete(); + d1d2.delete(); + d1.delete(); + testDir.delete(); + + assertTrue( ps.contains( "123/f1" ) ); + assertTrue( ps.contains( "/123/f1" ) ); + assertTrue( ps.contains( "123\\f1" ) ); + assertTrue( ps.contains( "123\\f2" ) ); + assertTrue( ps.contains( "\\123/d1\\d2/f1" ) ); + assertTrue( ps.contains( "123\\d1/d2\\f2" ) ); + assertFalse( ps.contains( "123\\f3" ) ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/WebappStructureTest.java b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/WebappStructureTest.java new file mode 100644 index 000000000..0e135de23 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/java/org/apache/maven/plugins/war/util/WebappStructureTest.java @@ -0,0 +1,114 @@ +package org.apache.maven.plugins.war.util; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import junit.framework.TestCase; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Dependency; +import org.apache.maven.plugins.war.util.WebappStructure; + +import java.util.ArrayList; + +/** + * @author Stephane Nicoll + */ +public class WebappStructureTest + extends TestCase +{ + public void testUnknownFileNotAvailable() + { + final WebappStructure structure = new WebappStructure( new ArrayList() ); + assertFalse( structure.isRegistered( "/foo/bar.txt" ) ); + } + + public void testRegisterSamePathTwice() + { + final WebappStructure structure = new WebappStructure( new ArrayList() ); + structure.registerFile( "overlay1", "WEB-INF/web.xml" ); + assertFalse( structure.registerFile( "currentBuild", "WEB-INF/web.xml" ) ); + } + + public void testRegisterForced() + { + final String path = "WEB-INF/web.xml"; + final WebappStructure structure = new WebappStructure( new ArrayList() ); + assertFalse("New file should return false", + structure.registerFileForced( "overlay1", path )); + assertEquals( "overlay1", structure.getOwner( path ) ); + } + + public void testRegisterSamePathTwiceForced() + { + final String path = "WEB-INF/web.xml"; + final WebappStructure structure = new WebappStructure( new ArrayList() ); + structure.registerFile( "overlay1", path ); + assertEquals( "overlay1", structure.getOwner( path ) ); + assertTrue("owner replacement should have returned true", + structure.registerFileForced( "currentBuild", path )); + assertEquals("currentBuild", structure.getOwner( path )); + } + + + protected Dependency createDependency( String groupId, String artifactId, String version, String type, String scope, + String classifier ) + { + final Dependency dep = new Dependency(); + dep.setGroupId( groupId ); + dep.setArtifactId( artifactId ); + dep.setVersion( version ); + if ( type == null ) + { + dep.setType( "jar" ); + } + else + { + dep.setType( type ); + } + if ( scope != null ) + { + dep.setScope( scope ); + } + else + { + dep.setScope( Artifact.SCOPE_COMPILE ); + } + if ( classifier != null ) + { + dep.setClassifier( classifier ); + } + return dep; + } + + protected Dependency createDependency( String groupId, String artifactId, String version, String type, + String scope ) + { + return createDependency( groupId, artifactId, version, type, scope, null ); + } + + protected Dependency createDependency( String groupId, String artifactId, String version, String type ) + { + return createDependency( groupId, artifactId, version, type, null ); + } + + protected Dependency createDependency( String groupId, String artifactId, String version ) + { + return createDependency( groupId, artifactId, version, null ); + } +} diff --git a/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/WEB-INF/web.xml new file mode 100644 index 000000000..c39450400 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + Sample one overlay + diff --git a/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/index.jsp b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/index.jsp new file mode 100644 index 000000000..8846f183a --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/index.jsp @@ -0,0 +1,25 @@ + + + +

+ Hello World, this is overlay-one! +

+ + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/login.jsp b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/login.jsp new file mode 100644 index 000000000..96199c36e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-one/login.jsp @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/WEB-INF/web.xml b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/WEB-INF/web.xml new file mode 100644 index 000000000..139f2c579 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/WEB-INF/web.xml @@ -0,0 +1,21 @@ + + + Sample two overlay + diff --git a/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/admin.jsp b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/admin.jsp new file mode 100644 index 000000000..96199c36e --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/admin.jsp @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/index.jsp b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/index.jsp new file mode 100644 index 000000000..d0fe77df5 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/overlays/overlay-two/index.jsp @@ -0,0 +1,25 @@ + + + +

+ Hello World, this is overlay-two! +

+ + \ No newline at end of file diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/ejb.jar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/ejb.jar new file mode 100644 index 0000000000000000000000000000000000000000..5ad4bcf96cea1035e2e9eb085b0ef117942ba0a3 GIT binary patch literal 753 zcmWIWW@Zs#-~htvrllqfNPv@pg~8V~#8KDN&rSc|DFy~+h5&DN4v-2asImZ@nni#r z;F^6M{XE@VgG2Ou-9G!CIql=Et9OytTUYDcne&^246YbIcv__A<*VcAd$DvC3+Id% zl1HVbv@%$xKhyk?rY8PWJX~!0l4oMt70*;(XMQUBSi}f+$Zw9>Ebc&SKp5l@F0iBc z8c}`00u;?JO4moI;73yd@)1ZyYE}}GDp535JV>e%3kvY4ltEMJSWpn0T3no&pQo3c zlUQ6F;LXS+!hjm)umA;xIVyk$A}9vXwIT-rD8v!K7RZEaMGAkA30w@gg9>57F(4BX za0ngPf)1hM1ylzlAQ8H-1tmfkA0s08Apwh9Cn#tUzyeJtWzF E0Krp!l>h($ literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/ejbclient.jar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/ejbclient.jar new file mode 100644 index 0000000000000000000000000000000000000000..5ad4bcf96cea1035e2e9eb085b0ef117942ba0a3 GIT binary patch literal 753 zcmWIWW@Zs#-~htvrllqfNPv@pg~8V~#8KDN&rSc|DFy~+h5&DN4v-2asImZ@nni#r z;F^6M{XE@VgG2Ou-9G!CIql=Et9OytTUYDcne&^246YbIcv__A<*VcAd$DvC3+Id% zl1HVbv@%$xKhyk?rY8PWJX~!0l4oMt70*;(XMQUBSi}f+$Zw9>Ebc&SKp5l@F0iBc z8c}`00u;?JO4moI;73yd@)1ZyYE}}GDp535JV>e%3kvY4ltEMJSWpn0T3no&pQo3c zlUQ6F;LXS+!hjm)umA;xIVyk$A}9vXwIT-rD8v!K7RZEaMGAkA30w@gg9>57F(4BX za0ngPf)1hM1ylzlAQ8H-1tmfkA0s08Apwh9Cn#tUzyeJtWzF E0Krp!l>h($ literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/include-exclude.war b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/include-exclude.war new file mode 100644 index 0000000000000000000000000000000000000000..4f580db08b4fe826bc0ac8446bdcdd27330b0008 GIT binary patch literal 1255 zcmWIWW@Zs#-~hr^rOQnikN_tG3xls~h@-BjpPT-_Qw$8u3<2Kk93T};P-Ou)HH!dM zz%~0i`gyv!28ZbRx_$ONbK1vSSMMUPx31Q?Gv_x48C)@b@U%$J%U8$K_hRWP7S0(j zB#%lG}v2y=Xzl4OLN`m|KvOilnd& zO`$NV!sPtig2cQ^Bn=u60v6qoagaI`;_<@F@$;OsRfkE5a23S^I&NQIoi<80i`+w-~}cnP`-nP2-IUR Va}a5el?|i^loVQkI()$i5C9+h=Uo5* literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/javax.servlet-api-3.0.1.jar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/javax.servlet-api-3.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..4e2edcc9df69742be7b55e19370dddb5b20fc451 GIT binary patch literal 85353 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%0Ar@y{D-hY*!XHbeQp%zeE{JQy1 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 literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/sample.par b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/sample.par new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple-updated.war b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple-updated.war new file mode 100644 index 0000000000000000000000000000000000000000..58a5b76b2c516ea812129c57cdc86de5dbce8b3b GIT binary patch literal 995 zcmWIWW@Zs#-~ht!c}qn0fj+;HzSh> z18Pjeq7f9+r~n>ypm;~uiX8Buct!wQAQP??Db_(Ia53PHAcP6WfJ{Vm;noF;Fa&r5 zWWsd8qYt-cRNqQ6!lDzm4p4+5fHhPHJbDot(c=pg!3dCosu2<0h`0e6fE;h22uFaa Z*bRV1e}FeD8%QxIJ3j>~eF4M_3;@^Tu_gcj literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.aar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.aar new file mode 100644 index 0000000000000000000000000000000000000000..79681e07a79b9e1882708f17c6e615db907b9182 GIT binary patch literal 969 zcmWIWW@Zs#-~ht!c}q2=HcP5@A4%PgrDu;u96XBM20`=vtA37ZjTaU<+iz zwIanW$OJA1+|htA;TVvKh!otqK+%E#Z-7jgE_fv2){N>~Nk&+t;no3)HUzMS>VQWg rLL;_lL}*Mw)rg2x+y;Q66#=GVHvkdItZX2~piKJ|sPr5VGcW)E1M{k& literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.jar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.jar new file mode 100644 index 0000000000000000000000000000000000000000..79681e07a79b9e1882708f17c6e615db907b9182 GIT binary patch literal 969 zcmWIWW@Zs#-~ht!c}q2=HcP5@A4%PgrDu;u96XBM20`=vtA37ZjTaU<+iz zwIanW$OJA1+|htA;TVvKh!otqK+%E#Z-7jgE_fv2){N>~Nk&+t;no3)HUzMS>VQWg rLL;_lL}*Mw)rg2x+y;Q66#=GVHvkdItZX2~piKJ|sPr5VGcW)E1M{k& literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.mar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.mar new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.war b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.war new file mode 100644 index 0000000000000000000000000000000000000000..79681e07a79b9e1882708f17c6e615db907b9182 GIT binary patch literal 969 zcmWIWW@Zs#-~ht!c}q2=HcP5@A4%PgrDu;u96XBM20`=vtA37ZjTaU<+iz zwIanW$OJA1+|htA;TVvKh!otqK+%E#Z-7jgE_fv2){N>~Nk&+t;no3)HUzMS>VQWg rLL;_lL}*Mw)rg2x+y;Q66#=GVHvkdItZX2~piKJ|sPr5VGcW)E1M{k& literal 0 HcmV?d00001 diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.xar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/simple.xar new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/tld.jar b/Java-base/maven-war-plugin/src/src/test/resources/unit/sample_wars/tld.jar new file mode 100644 index 000000000..e69de29bb diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedinplacemojo/plugin-config.xml b/Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedinplacemojo/plugin-config.xml new file mode 100644 index 000000000..f5a51e6cd --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedinplacemojo/plugin-config.xml @@ -0,0 +1,31 @@ + + + + war-plugin-test + + + + maven-war-plugin + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedmojo/plugin-config.xml b/Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedmojo/plugin-config.xml new file mode 100644 index 000000000..f5a51e6cd --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/unit/warexplodedmojo/plugin-config.xml @@ -0,0 +1,31 @@ + + + + war-plugin-test + + + + maven-war-plugin + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/not-primary-artifact.xml b/Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/not-primary-artifact.xml new file mode 100644 index 000000000..902e78b33 --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/not-primary-artifact.xml @@ -0,0 +1,32 @@ + + + + war-plugin-test + + + + maven-war-plugin + + false + + + + + diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/plugin-config-primary-artifact.xml b/Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/plugin-config-primary-artifact.xml new file mode 100644 index 000000000..684d0c79b --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/unit/warmojotest/plugin-config-primary-artifact.xml @@ -0,0 +1,32 @@ + + + + war-plugin-test + + + + maven-war-plugin + + true + + + + + diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/waroverlays/default.xml b/Java-base/maven-war-plugin/src/src/test/resources/unit/waroverlays/default.xml new file mode 100644 index 000000000..de295beaf --- /dev/null +++ b/Java-base/maven-war-plugin/src/src/test/resources/unit/waroverlays/default.xml @@ -0,0 +1,32 @@ + + + + war-plugin-test + + + + maven-war-plugin + + + + + + + diff --git a/Java-base/maven-war-plugin/src/src/test/resources/unit/warziptest/foobar.zip b/Java-base/maven-war-plugin/src/src/test/resources/unit/warziptest/foobar.zip new file mode 100755 index 0000000000000000000000000000000000000000..e1eb8480c0a5b7a23c28190872c8e6ce7a6b2280 GIT binary patch literal 453 zcmWIWW@h1H00HeaC(IO6i+h-XY!GG#;tX9iRUl!5~dAj>5`ximL5 zucTNZF)u|SIJKlCGcUbZKR+iiw^HA!v?w*PR6i-PNFQN}37RR~KvO^}fEZ*WLLs_! zEKr33-i%Bl47i;Ravvju0*C?vs7H`Bp&JbH2LuQJDbjp}a0)hmflP%sB@f6#@g+hd ex^qDOgaD9prUNk~09n~UqAWo84M^_ + + + war-with-one-zip + + + + maven-war-plugin + + + + + + diff --git a/Java/maven-war-plugin-AbstractArtifactStub_103/Dockerfile b/Java/maven-war-plugin-AbstractArtifactStub_103/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_103/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-AbstractArtifactStub_103/buggy.java b/Java/maven-war-plugin-AbstractArtifactStub_103/buggy.java new file mode 100644 index 000000000..6163fae79 --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_103/buggy.java @@ -0,0 +1,147 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; + +public abstract class AbstractArtifactStub + extends ArtifactStub +{ + protected String basedir; + + public AbstractArtifactStub( String _basedir ) + { + basedir = _basedir; + } + + public String getVersion() + { + return "0.0-Test"; + } + + @Override + public String getBaseVersion() + { + return getVersion(); + } + + public String getScope() + { + return Artifact.SCOPE_RUNTIME; + } + + public VersionRange getVersionRange() + { + return VersionRange.createFromVersion( getVersion()); + } + + public boolean isOptional() + { + return false; + } + + public ArtifactHandler getArtifactHandler() + { + return new DefaultArtifactHandler( getType() ); + } + + /* + * TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... + */ +/* TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... */ +public int compareTo(org.apache.maven.artifact.Artifact a) { + /* -- We need to support groupId=null (it is missing in DefaultArtifact.java) */ + int result; + if (a.getGroupId() != null) { + result = getGroupId().compareTo(a.getGroupId()); + } else { + result = (getGroupId() == null) ? 0 : -1; + } + /* -- */ + if (result == 0) { + result = getArtifactId().compareTo(a.getArtifactId()); + if (result == 0) { + result = getType().compareTo(a.getType()); + if (result == 0) { + { + { + result = /* NPEX_NULL_EXP */ + getClassifier().compareTo(/* NPEX_NULL_EXP */ + a.getClassifier()); + } + } + if (result == 0) { + // We don't consider the version range in the comparison, just the resolved version + result = getVersion().compareTo(a.getVersion()); + } + } + } + } + return result; +} + + /* + * TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... + */ + public boolean equals( Object o ) + { + if ( o == this ) + { + return true; + } + + if ( !( o instanceof Artifact ) ) + { + return false; + } + + Artifact a = (Artifact) o; + + /* -- We need to support groupId=null (it is missing in DefaultArtifact.java) */ + if ( a.getGroupId() == null ? ( getGroupId() != null ) : a.getGroupId().equals( getGroupId() ) ) + { + return false; + } + else if ( !a.getArtifactId().equals( getArtifactId() ) ) + { + return false; + } + else if ( !a.getVersion().equals( getVersion() ) ) + { + return false; + } + else if ( !a.getType().equals( getType() ) ) + { + return false; + } + else if ( a.getClassifier() == null ? getClassifier() != null : !a.getClassifier().equals( getClassifier() ) ) + { + return false; + } + + // We don't consider the version range in the comparison, just the resolved version + + return true; + } +} diff --git a/Java/maven-war-plugin-AbstractArtifactStub_103/metadata.json b/Java/maven-war-plugin-AbstractArtifactStub_103/metadata.json new file mode 100644 index 000000000..b68d55db7 --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_103/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-AbstractArtifactStub_103", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java", + "line": 91, + "npe_method": "compareTo", + "deref_field": "getClassifier", + "npe_class": "AbstractArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "AbstractArtifactStub_103" + } +} diff --git a/Java/maven-war-plugin-AbstractArtifactStub_103/npe.json b/Java/maven-war-plugin-AbstractArtifactStub_103/npe.json new file mode 100644 index 000000000..02cda04e0 --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_103/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java", + "line": 91, + "npe_method": "compareTo", + "deref_field": "getClassifier", + "npe_class": "AbstractArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-AbstractArtifactStub_76/Dockerfile b/Java/maven-war-plugin-AbstractArtifactStub_76/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_76/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-AbstractArtifactStub_76/buggy.java b/Java/maven-war-plugin-AbstractArtifactStub_76/buggy.java new file mode 100644 index 000000000..314b71093 --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_76/buggy.java @@ -0,0 +1,148 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.DefaultArtifactHandler; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.plugin.testing.stubs.ArtifactStub; + +public abstract class AbstractArtifactStub + extends ArtifactStub +{ + protected String basedir; + + public AbstractArtifactStub( String _basedir ) + { + basedir = _basedir; + } + + public String getVersion() + { + return "0.0-Test"; + } + + @Override + public String getBaseVersion() + { + return getVersion(); + } + + public String getScope() + { + return Artifact.SCOPE_RUNTIME; + } + + public VersionRange getVersionRange() + { + return VersionRange.createFromVersion( getVersion()); + } + + public boolean isOptional() + { + return false; + } + + public ArtifactHandler getArtifactHandler() + { + return new DefaultArtifactHandler( getType() ); + } + + /* + * TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... + */ +/* TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... */ +public int compareTo(org.apache.maven.artifact.Artifact a) { + /* -- We need to support groupId=null (it is missing in DefaultArtifact.java) */ + int result; + { + result = getGroupId().compareTo(/* NPEX_NULL_EXP */ + a.getGroupId()); + } + /* -- */ + if (result == 0) { + result = getArtifactId().compareTo(a.getArtifactId()); + if (result == 0) { + result = getType().compareTo(a.getType()); + if (result == 0) { + if (getClassifier() == null) { + if (a.getClassifier() != null) { + result = 1; + } + } else if (a.getClassifier() != null) { + result = getClassifier().compareTo(a.getClassifier()); + } else { + result = -1; + } + if (result == 0) { + // We don't consider the version range in the comparison, just the resolved version + result = getVersion().compareTo(a.getVersion()); + } + } + } + } + return result; +} + + /* + * TODO: Copied from org/apache/maven/artifact/DefaultArtifact.java; Consider merging... + */ + public boolean equals( Object o ) + { + if ( o == this ) + { + return true; + } + + if ( !( o instanceof Artifact ) ) + { + return false; + } + + Artifact a = (Artifact) o; + + /* -- We need to support groupId=null (it is missing in DefaultArtifact.java) */ + if ( a.getGroupId() == null ? ( getGroupId() != null ) : a.getGroupId().equals( getGroupId() ) ) + { + return false; + } + else if ( !a.getArtifactId().equals( getArtifactId() ) ) + { + return false; + } + else if ( !a.getVersion().equals( getVersion() ) ) + { + return false; + } + else if ( !a.getType().equals( getType() ) ) + { + return false; + } + else if ( a.getClassifier() == null ? getClassifier() != null : !a.getClassifier().equals( getClassifier() ) ) + { + return false; + } + + // We don't consider the version range in the comparison, just the resolved version + + return true; + } +} diff --git a/Java/maven-war-plugin-AbstractArtifactStub_76/metadata.json b/Java/maven-war-plugin-AbstractArtifactStub_76/metadata.json new file mode 100644 index 000000000..42e10b7b6 --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_76/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-AbstractArtifactStub_76", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java", + "line": 78, + "npe_method": "compareTo", + "deref_field": "getGroupId", + "npe_class": "AbstractArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "AbstractArtifactStub_76" + } +} diff --git a/Java/maven-war-plugin-AbstractArtifactStub_76/npe.json b/Java/maven-war-plugin-AbstractArtifactStub_76/npe.json new file mode 100644 index 000000000..b338e3e39 --- /dev/null +++ b/Java/maven-war-plugin-AbstractArtifactStub_76/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/AbstractArtifactStub.java", + "line": 78, + "npe_method": "compareTo", + "deref_field": "getGroupId", + "npe_class": "AbstractArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/Dockerfile b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/buggy.java b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/buggy.java new file mode 100644 index 000000000..90dc65e98 --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/buggy.java @@ -0,0 +1,304 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Stephane Nicoll + */ +public abstract class AbstractWarExplodedMojoTest + extends AbstractWarMojoTest +{ + + protected WarExplodedMojo mojo; + + public void setUp() + throws Exception + { + super.setUp(); + mojo = (WarExplodedMojo) lookupMojo( "exploded", getPomFile() ); + } + + /** + * Returns the pom configuration to use. + * + * @return the pom configuration + */ + protected abstract File getPomFile(); + + /** + * Returns the test directory to use. + * + * @return the test directory + */ + protected abstract File getTestDirectory(); + + /** + * Configures the exploded mojo for the specified test. + * + * If the sourceFiles parameter is null, sample JSPs are created by default. + * + * @param testId the id of the test + * @param artifactStubs the dependencies (may be null) + * @param sourceFiles the source files to create (may be null) + * @return the webapp directory + * @throws Exception if an error occurs while configuring the mojo + */ +/** + * Configures the exploded mojo for the specified test. + * + * If the sourceFiles parameter is null, sample JSPs are created by default. + * + * @param testId + * the id of the test + * @param artifactStubs + * the dependencies (may be null) + * @param sourceFiles + * the source files to create (may be null) + * @return the webapp directory + * @throws Exception + * if an error occurs while configuring the mojo + */ +protected java.io.File setUpMojo(final java.lang.String testId, org.apache.maven.plugin.testing.stubs.ArtifactStub[] artifactStubs, java.lang.String[] sourceFiles) throws java.lang.Exception { + final org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub project = new org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub(); + final java.io.File webAppDirectory = new java.io.File(getTestDirectory(), testId); + // Create the webapp sources + java.io.File webAppSource; + if (sourceFiles == null) { + webAppSource = createWebAppSource(testId); + } else { + webAppSource = createWebAppSource(testId, false); + for (java.lang.String sourceFile : sourceFiles) { + java.io.File sample = new java.io.File(webAppSource, sourceFile); + createFile(sample); + } + } + final java.io.File classesDir = createClassesDir(testId, true); + final java.io.File workDirectory = new java.io.File(getTestDirectory(), "/war/work-" + testId); + createDir(workDirectory); + { + for (org.apache.maven.plugin.testing.stubs.ArtifactStub artifactStub : /* NPEX_NULL_EXP */ + artifactStubs) { + project.addArtifact(artifactStub); + } + } + configureMojo(mojo, new java.util.LinkedList(), classesDir, webAppSource, webAppDirectory, project); + setVariableValueToObject(mojo, "workDirectory", workDirectory); + return webAppDirectory; +} + + /** + * Configures the exploded mojo for the specified test. + * + * @param testId the id of the test + * @param artifactStubs the dependencies (may be null) + * @return the webapp directory + * @throws Exception if an error occurs while configuring the mojo + */ + protected File setUpMojo( final String testId, ArtifactStub[] artifactStubs ) + throws Exception + { + return setUpMojo( testId, artifactStubs, null ); + } + + /** + * Cleans up a directory. + * + * @param directory the directory to remove + * @throws IOException if an error occurred while removing the directory + */ + protected void cleanDirectory( File directory ) + throws IOException + { + if ( directory != null && directory.isDirectory() && directory.exists() ) + { + FileUtils.deleteDirectory( directory ); + } + } + + /** + * Asserts the default content of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @return a list of File objects that have been asserted + */ + protected List assertDefaultContent( File webAppDirectory ) + { + // Validate content of the webapp + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + + assertTrue( "source file not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source file not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + + final List content = new ArrayList<>(); + content.add( expectedWebSourceFile ); + content.add( expectedWebSource2File ); + + return content; + } + + /** + * Asserts the web.xml file of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @return a list with the web.xml File object + */ + protected List assertWebXml( File webAppDirectory ) + { + File expectedWEBXMLFile = new File( webAppDirectory, "WEB-INF/web.xml" ); + assertTrue( "web xml not found: " + expectedWEBXMLFile.toString(), expectedWEBXMLFile.exists() ); + + final List content = new ArrayList<>(); + content.add( expectedWEBXMLFile ); + + return content; + } + + /** + * Asserts custom content of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @param filePaths an array of file paths relative to the webapp directory + * @param customMessage a custom message if an assertion fails + * @return a list of File objects that have been inspected + */ + protected List assertCustomContent( File webAppDirectory, String[] filePaths, String customMessage ) + { + final List content = new ArrayList<>(); + for ( String filePath : filePaths ) + { + final File expectedFile = new File( webAppDirectory, filePath ); + if ( customMessage != null ) + { + assertTrue( customMessage + " - " + expectedFile.toString(), expectedFile.exists() ); + } + else + { + assertTrue( "source file not found: " + expectedFile.toString(), expectedFile.exists() ); + } + content.add( expectedFile ); + } + return content; + } + + /** + * Asserts that the webapp contains only the specified files. + * + * @param webAppDirectory the webapp directory + * @param expectedFiles the expected files + * @param filter an optional filter to ignore some resources + */ + protected void assertWebAppContent( File webAppDirectory, List expectedFiles, FileFilter filter ) + { + final List webAppContent = new ArrayList<>(); + if ( filter != null ) + { + buildFilesList( webAppDirectory, filter, webAppContent ); + } + else + { + buildFilesList( webAppDirectory, new FileFilterImpl( webAppDirectory, null ), webAppContent ); + } + + // Now we have the files, sort them. + Collections.sort( expectedFiles ); + Collections.sort( webAppContent ); + assertEquals( "Invalid webapp content, expected " + expectedFiles.size() + "file(s) " + expectedFiles + + " but got " + webAppContent.size() + " file(s) " + webAppContent, expectedFiles, webAppContent ); + } + + /** + * Builds the list of files and directories from the specified dir. + * + * Note that the filter is not used the usual way. If the filter does not accept the current file, it's not added + * but yet the subdirectories are added if any. + * + * @param dir the base directory + * @param filter the filter + * @param content the current content, updated recursively + */ + private void buildFilesList( final File dir, FileFilter filter, final List content ) + { + final File[] files = dir.listFiles(); + + for ( File file : files ) + { + // Add the file if the filter is ok with it + if ( filter.accept( file ) ) + { + content.add( file ); + } + + // Even if the file is not accepted and is a directory, add it + if ( file.isDirectory() ) + { + buildFilesList( file, filter, content ); + } + + } + } + + class FileFilterImpl + implements FileFilter + { + + private final List rejectedFilePaths; + + private final int webAppDirIndex; + + public FileFilterImpl( File webAppDirectory, String[] rejectedFilePaths ) + { + if ( rejectedFilePaths != null ) + { + this.rejectedFilePaths = Arrays.asList( rejectedFilePaths ); + } + else + { + this.rejectedFilePaths = new ArrayList<>(); + } + this.webAppDirIndex = webAppDirectory.getAbsolutePath().length() + 1; + } + + public boolean accept( File file ) + { + String effectiveRelativePath = buildRelativePath( file ); + return !( rejectedFilePaths.contains( effectiveRelativePath ) || file.isDirectory() ); + } + + private String buildRelativePath( File f ) + { + return f.getAbsolutePath().substring( webAppDirIndex ); + } + } + +} diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/metadata.json b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/metadata.json new file mode 100644 index 000000000..c9aac8575 --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-AbstractWarExplodedMojoTest_104", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java", + "line": 110, + "npe_method": "setUpMojo", + "deref_field": "artifactStubs", + "npe_class": "AbstractWarExplodedMojoTest", + "repo": "maven-war-plugin", + "bug_id": "AbstractWarExplodedMojoTest_104" + } +} diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/npe.json b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/npe.json new file mode 100644 index 000000000..a413444cb --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_104/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java", + "line": 110, + "npe_method": "setUpMojo", + "deref_field": "artifactStubs", + "npe_class": "AbstractWarExplodedMojoTest" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/Dockerfile b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/buggy.java b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/buggy.java new file mode 100644 index 000000000..12a483f54 --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/buggy.java @@ -0,0 +1,302 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.testing.stubs.ArtifactStub; +import org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author Stephane Nicoll + */ +public abstract class AbstractWarExplodedMojoTest + extends AbstractWarMojoTest +{ + + protected WarExplodedMojo mojo; + + public void setUp() + throws Exception + { + super.setUp(); + mojo = (WarExplodedMojo) lookupMojo( "exploded", getPomFile() ); + } + + /** + * Returns the pom configuration to use. + * + * @return the pom configuration + */ + protected abstract File getPomFile(); + + /** + * Returns the test directory to use. + * + * @return the test directory + */ + protected abstract File getTestDirectory(); + + /** + * Configures the exploded mojo for the specified test. + * + * If the sourceFiles parameter is null, sample JSPs are created by default. + * + * @param testId the id of the test + * @param artifactStubs the dependencies (may be null) + * @param sourceFiles the source files to create (may be null) + * @return the webapp directory + * @throws Exception if an error occurs while configuring the mojo + */ +/** + * Configures the exploded mojo for the specified test. + * + * If the sourceFiles parameter is null, sample JSPs are created by default. + * + * @param testId + * the id of the test + * @param artifactStubs + * the dependencies (may be null) + * @param sourceFiles + * the source files to create (may be null) + * @return the webapp directory + * @throws Exception + * if an error occurs while configuring the mojo + */ +protected java.io.File setUpMojo(final java.lang.String testId, org.apache.maven.plugin.testing.stubs.ArtifactStub[] artifactStubs, java.lang.String[] sourceFiles) throws java.lang.Exception { + final org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub project = new org.apache.maven.plugins.war.stub.MavenProjectArtifactsStub(); + final java.io.File webAppDirectory = new java.io.File(getTestDirectory(), testId); + // Create the webapp sources + java.io.File webAppSource; + { + webAppSource = createWebAppSource(testId, false); + for (java.lang.String sourceFile : /* NPEX_NULL_EXP */ + sourceFiles) { + java.io.File sample = new java.io.File(webAppSource, sourceFile); + createFile(sample); + } + } + final java.io.File classesDir = createClassesDir(testId, true); + final java.io.File workDirectory = new java.io.File(getTestDirectory(), "/war/work-" + testId); + createDir(workDirectory); + if (artifactStubs != null) { + for (org.apache.maven.plugin.testing.stubs.ArtifactStub artifactStub : artifactStubs) { + project.addArtifact(artifactStub); + } + } + configureMojo(mojo, new java.util.LinkedList(), classesDir, webAppSource, webAppDirectory, project); + setVariableValueToObject(mojo, "workDirectory", workDirectory); + return webAppDirectory; +} + + /** + * Configures the exploded mojo for the specified test. + * + * @param testId the id of the test + * @param artifactStubs the dependencies (may be null) + * @return the webapp directory + * @throws Exception if an error occurs while configuring the mojo + */ + protected File setUpMojo( final String testId, ArtifactStub[] artifactStubs ) + throws Exception + { + return setUpMojo( testId, artifactStubs, null ); + } + + /** + * Cleans up a directory. + * + * @param directory the directory to remove + * @throws IOException if an error occurred while removing the directory + */ + protected void cleanDirectory( File directory ) + throws IOException + { + if ( directory != null && directory.isDirectory() && directory.exists() ) + { + FileUtils.deleteDirectory( directory ); + } + } + + /** + * Asserts the default content of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @return a list of File objects that have been asserted + */ + protected List assertDefaultContent( File webAppDirectory ) + { + // Validate content of the webapp + File expectedWebSourceFile = new File( webAppDirectory, "pansit.jsp" ); + File expectedWebSource2File = new File( webAppDirectory, "org/web/app/last-exile.jsp" ); + + assertTrue( "source file not found: " + expectedWebSourceFile.toString(), expectedWebSourceFile.exists() ); + assertTrue( "source file not found: " + expectedWebSource2File.toString(), expectedWebSource2File.exists() ); + + final List content = new ArrayList<>(); + content.add( expectedWebSourceFile ); + content.add( expectedWebSource2File ); + + return content; + } + + /** + * Asserts the web.xml file of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @return a list with the web.xml File object + */ + protected List assertWebXml( File webAppDirectory ) + { + File expectedWEBXMLFile = new File( webAppDirectory, "WEB-INF/web.xml" ); + assertTrue( "web xml not found: " + expectedWEBXMLFile.toString(), expectedWEBXMLFile.exists() ); + + final List content = new ArrayList<>(); + content.add( expectedWEBXMLFile ); + + return content; + } + + /** + * Asserts custom content of the war based on the specified webapp directory. + * + * @param webAppDirectory the webapp directory + * @param filePaths an array of file paths relative to the webapp directory + * @param customMessage a custom message if an assertion fails + * @return a list of File objects that have been inspected + */ + protected List assertCustomContent( File webAppDirectory, String[] filePaths, String customMessage ) + { + final List content = new ArrayList<>(); + for ( String filePath : filePaths ) + { + final File expectedFile = new File( webAppDirectory, filePath ); + if ( customMessage != null ) + { + assertTrue( customMessage + " - " + expectedFile.toString(), expectedFile.exists() ); + } + else + { + assertTrue( "source file not found: " + expectedFile.toString(), expectedFile.exists() ); + } + content.add( expectedFile ); + } + return content; + } + + /** + * Asserts that the webapp contains only the specified files. + * + * @param webAppDirectory the webapp directory + * @param expectedFiles the expected files + * @param filter an optional filter to ignore some resources + */ + protected void assertWebAppContent( File webAppDirectory, List expectedFiles, FileFilter filter ) + { + final List webAppContent = new ArrayList<>(); + if ( filter != null ) + { + buildFilesList( webAppDirectory, filter, webAppContent ); + } + else + { + buildFilesList( webAppDirectory, new FileFilterImpl( webAppDirectory, null ), webAppContent ); + } + + // Now we have the files, sort them. + Collections.sort( expectedFiles ); + Collections.sort( webAppContent ); + assertEquals( "Invalid webapp content, expected " + expectedFiles.size() + "file(s) " + expectedFiles + + " but got " + webAppContent.size() + " file(s) " + webAppContent, expectedFiles, webAppContent ); + } + + /** + * Builds the list of files and directories from the specified dir. + * + * Note that the filter is not used the usual way. If the filter does not accept the current file, it's not added + * but yet the subdirectories are added if any. + * + * @param dir the base directory + * @param filter the filter + * @param content the current content, updated recursively + */ + private void buildFilesList( final File dir, FileFilter filter, final List content ) + { + final File[] files = dir.listFiles(); + + for ( File file : files ) + { + // Add the file if the filter is ok with it + if ( filter.accept( file ) ) + { + content.add( file ); + } + + // Even if the file is not accepted and is a directory, add it + if ( file.isDirectory() ) + { + buildFilesList( file, filter, content ); + } + + } + } + + class FileFilterImpl + implements FileFilter + { + + private final List rejectedFilePaths; + + private final int webAppDirIndex; + + public FileFilterImpl( File webAppDirectory, String[] rejectedFilePaths ) + { + if ( rejectedFilePaths != null ) + { + this.rejectedFilePaths = Arrays.asList( rejectedFilePaths ); + } + else + { + this.rejectedFilePaths = new ArrayList<>(); + } + this.webAppDirIndex = webAppDirectory.getAbsolutePath().length() + 1; + } + + public boolean accept( File file ) + { + String effectiveRelativePath = buildRelativePath( file ); + return !( rejectedFilePaths.contains( effectiveRelativePath ) || file.isDirectory() ); + } + + private String buildRelativePath( File f ) + { + return f.getAbsolutePath().substring( webAppDirIndex ); + } + } + +} diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/metadata.json b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/metadata.json new file mode 100644 index 000000000..af40fb8f2 --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-AbstractWarExplodedMojoTest_84", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java", + "line": 99, + "npe_method": "setUpMojo", + "deref_field": "sourceFiles", + "npe_class": "AbstractWarExplodedMojoTest", + "repo": "maven-war-plugin", + "bug_id": "AbstractWarExplodedMojoTest_84" + } +} diff --git a/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/npe.json b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/npe.json new file mode 100644 index 000000000..f1b3686da --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarExplodedMojoTest_84/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/AbstractWarExplodedMojoTest.java", + "line": 99, + "npe_method": "setUpMojo", + "deref_field": "sourceFiles", + "npe_class": "AbstractWarExplodedMojoTest" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-AbstractWarPackagingTask_99/Dockerfile b/Java/maven-war-plugin-AbstractWarPackagingTask_99/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarPackagingTask_99/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-AbstractWarPackagingTask_99/buggy.java b/Java/maven-war-plugin-AbstractWarPackagingTask_99/buggy.java new file mode 100644 index 000000000..b1d0f3bab --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarPackagingTask_99/buggy.java @@ -0,0 +1,506 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.input.XmlStreamReader; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.util.PathSet; +import org.apache.maven.plugins.war.util.WebappStructure; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.apache.maven.shared.mapping.MappingUtils; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.UnArchiver; +import org.codehaus.plexus.archiver.jar.JarArchiver; +import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; + +/** + * @author Stephane Nicoll + */ +public abstract class AbstractWarPackagingTask + implements WarPackagingTask +{ + /** + * The default list of includes. + */ + public static final String[] DEFAULT_INCLUDES = { "**/**" }; + + /** + * The {@code WEB-INF} path. + */ + public static final String WEB_INF_PATH = "WEB-INF"; + + /** + * The {@code META-INF} path. + */ + public static final String META_INF_PATH = "META-INF"; + + /** + * The {@code classes} path. + */ + public static final String CLASSES_PATH = "WEB-INF/classes/"; + + /** + * The {@code lib} path. + */ + public static final String LIB_PATH = "WEB-INF/lib/"; + + /** + * Copies the files if possible with an optional target prefix. + * + * Copy uses a first-win strategy: files that have already been copied by previous tasks are ignored. This method + * makes sure to update the list of protected files which gives the list of files that have already been copied. + * + * If the structure of the source directory is not the same as the root of the webapp, use the targetPrefix + * parameter to specify in which particular directory the files should be copied. Use null to copy the + * files with the same structure + * + * @param sourceId the source id + * @param context the context to use + * @param sourceBaseDir the base directory from which the sourceFilesSet will be copied + * @param sourceFilesSet the files to be copied + * @param targetPrefix the prefix to add to the target file name + * @param filtered filter or not. + * @throws IOException if an error occurred while copying the files + * @throws MojoExecutionException if an error occurs. + */ +/** + * Copies the files if possible with an optional target prefix. + * + * Copy uses a first-win strategy: files that have already been copied by previous tasks are ignored. This method + * makes sure to update the list of protected files which gives the list of files that have already been copied. + * + * If the structure of the source directory is not the same as the root of the webapp, use the targetPrefix + * parameter to specify in which particular directory the files should be copied. Use null to copy the + * files with the same structure + * + * @param sourceId + * the source id + * @param context + * the context to use + * @param sourceBaseDir + * the base directory from which the sourceFilesSet will be copied + * @param sourceFilesSet + * the files to be copied + * @param targetPrefix + * the prefix to add to the target file name + * @param filtered + * filter or not. + * @throws IOException + * if an error occurred while copying the files + * @throws MojoExecutionException + * if an error occurs. + */ +protected void copyFiles(java.lang.String sourceId, org.apache.maven.plugins.war.packaging.WarPackagingContext context, java.io.File sourceBaseDir, org.apache.maven.plugins.war.util.PathSet sourceFilesSet, java.lang.String targetPrefix, boolean filtered) throws java.io.IOException, org.apache.maven.plugin.MojoExecutionException { + for (java.lang.String fileToCopyName : sourceFilesSet.paths()) { + final java.io.File sourceFile = new java.io.File(sourceBaseDir, fileToCopyName); + java.lang.String destinationFileName; + { + destinationFileName = /* NPEX_NULL_EXP */ + targetPrefix + fileToCopyName; + } + if (filtered && (!context.isNonFilteredExtension(sourceFile.getName()))) { + copyFilteredFile(sourceId, context, sourceFile, destinationFileName); + } else { + copyFile(sourceId, context, sourceFile, destinationFileName); + } + } +} + + /** + * Copies the files if possible as is. + * + * Copy uses a first-win strategy: files that have already been copied by previous tasks are ignored. This method + * makes sure to update the list of protected files which gives the list of files that have already been copied. + * + * @param sourceId the source id + * @param context the context to use + * @param sourceBaseDir the base directory from which the sourceFilesSet will be copied + * @param sourceFilesSet the files to be copied + * @param filtered filter or not. + * @throws IOException if an error occurred while copying the files + * @throws MojoExecutionException break the build. + */ + protected void copyFiles( String sourceId, WarPackagingContext context, File sourceBaseDir, PathSet sourceFilesSet, + boolean filtered ) + throws IOException, MojoExecutionException + { + copyFiles( sourceId, context, sourceBaseDir, sourceFilesSet, null, filtered ); + } + + /** + * Copy the specified file if the target location has not yet already been used. + * + * The targetFileName is the relative path according to the root of the generated web application. + * + * @param sourceId the source id + * @param context the context to use + * @param file the file to copy + * @param targetFilename the relative path according to the root of the webapp + * @throws IOException if an error occurred while copying + */ + // CHECKSTYLE_OFF: LineLength + protected void copyFile( String sourceId, final WarPackagingContext context, final File file, String targetFilename ) + throws IOException + // CHECKSTYLE_ON: LineLength + { + final File targetFile = new File( context.getWebappDirectory(), targetFilename ); + + if ( file.isFile() ) + { + context.getWebappStructure().registerFile( sourceId, targetFilename, + new WebappStructure.RegistrationCallback() + { + public void registered( String ownerId, String targetFilename ) + throws IOException + { + copyFile( context, file, targetFile, targetFilename, + false ); + } + + public void alreadyRegistered( String ownerId, + String targetFilename ) + throws IOException + { + copyFile( context, file, targetFile, targetFilename, + true ); + } + + public void refused( String ownerId, String targetFilename, + String actualOwnerId ) + throws IOException + { + context.getLog().debug( " - " + + targetFilename + + " wasn't copied because it has " + + "already been packaged for overlay [" + + actualOwnerId + "]." ); + } + + public void superseded( String ownerId, + String targetFilename, + String deprecatedOwnerId ) + throws IOException + { + context.getLog().info( "File [" + + targetFilename + + "] belonged to overlay [" + + deprecatedOwnerId + + "] so it will be overwritten." ); + copyFile( context, file, targetFile, targetFilename, + false ); + } + + public void supersededUnknownOwner( String ownerId, + String targetFilename, + String unknownOwnerId ) + throws IOException + { + // CHECKSTYLE_OFF: LineLength + context.getLog().warn( "File [" + + targetFilename + + "] belonged to overlay [" + + unknownOwnerId + + "] which does not exist anymore in the current project. It is recommended to invoke " + + "clean if the dependencies of the project changed." ); + // CHECKSTYLE_ON: LineLength + copyFile( context, file, targetFile, targetFilename, + false ); + } + } ); + } + else if ( !targetFile.exists() && !targetFile.mkdirs() ) + { + context.getLog().info( "Failed to create directory " + targetFile.getAbsolutePath() ); + } + } + + /** + * Copy the specified file if the target location has not yet already been used and filter its content with the + * configured filter properties. + * + * The targetFileName is the relative path according to the root of the generated web application. + * + * @param sourceId the source id + * @param context the context to use + * @param file the file to copy + * @param targetFilename the relative path according to the root of the webapp + * @return true if the file has been copied, false otherwise + * @throws IOException if an error occurred while copying + * @throws MojoExecutionException if an error occurred while retrieving the filter properties + */ + protected boolean copyFilteredFile( String sourceId, final WarPackagingContext context, File file, + String targetFilename ) + throws IOException, MojoExecutionException + { + context.addResource( targetFilename ); + + if ( context.getWebappStructure().registerFile( sourceId, targetFilename ) ) + { + final File targetFile = new File( context.getWebappDirectory(), targetFilename ); + final String encoding; + try + { + if ( isXmlFile( file ) ) + { + // For xml-files we extract the encoding from the files + encoding = getEncoding( file ); + } + else + { + // For all others we use the configured encoding + encoding = context.getResourceEncoding(); + } + // fix for MWAR-36, ensures that the parent dir are created first + targetFile.getParentFile().mkdirs(); + + context.getMavenFileFilter().copyFile( file, targetFile, true, context.getFilterWrappers(), encoding ); + } + catch ( MavenFilteringException e ) + { + throw new MojoExecutionException( e.getMessage(), e ); + } + // CHECKSTYLE_OFF: LineLength + // Add the file to the protected list + context.getLog().debug( " + " + targetFilename + " has been copied (filtered encoding='" + encoding + "')." ); + // CHECKSTYLE_ON: LineLength + return true; + } + else + { + context.getLog().debug( " - " + targetFilename + + " wasn't copied because it has already been packaged (filtered)." ); + return false; + } + } + + /** + * Unpacks the specified file to the specified directory. + * + * @param context the packaging context + * @param file the file to unpack + * @param unpackDirectory the directory to use for th unpacked file + * @throws MojoExecutionException if an error occurred while unpacking the file + */ + protected void doUnpack( WarPackagingContext context, File file, File unpackDirectory ) + throws MojoExecutionException + { + String archiveExt = FileUtils.getExtension( file.getAbsolutePath() ).toLowerCase(); + + try + { + UnArchiver unArchiver = context.getArchiverManager().getUnArchiver( archiveExt ); + unArchiver.setSourceFile( file ); + unArchiver.setDestDirectory( unpackDirectory ); + unArchiver.setOverwrite( true ); + unArchiver.extract(); + } + catch ( ArchiverException e ) + { + throw new MojoExecutionException( "Error unpacking file [" + file.getAbsolutePath() + "]" + " to [" + + unpackDirectory.getAbsolutePath() + "]", e ); + } + catch ( NoSuchArchiverException e ) + { + context.getLog().warn( "Skip unpacking dependency file [" + file.getAbsolutePath() + + " with unknown extension [" + archiveExt + "]" ); + } + } + + /** + * Copy file from source to destination. The directories up to destination will be created if they + * don't already exist. if the onlyIfModified flag is false, destination will be + * overwritten if it already exists. If the flag is true destination will be overwritten if it's not up to + * date. + * + * @param context the packaging context + * @param source an existing non-directory File to copy bytes from + * @param destination a non-directory File to write bytes to (possibly overwriting). + * @param targetFilename the relative path of the file from the webapp root directory + * @param onlyIfModified if true, copy the file only if the source has changed, always copy otherwise + * @return true if the file has been copied/updated, false otherwise + * @throws IOException if source does not exist, destination cannot be written to, or an + * IO error occurs during copying + */ + protected boolean copyFile( WarPackagingContext context, File source, File destination, String targetFilename, + boolean onlyIfModified ) + throws IOException + { + context.addResource( targetFilename ); + + if ( onlyIfModified && destination.lastModified() >= source.lastModified() ) + { + context.getLog().debug( " * " + targetFilename + " is up to date." ); + return false; + } + else + { + if ( source.isDirectory() ) + { + context.getLog().warn( " + " + targetFilename + " is packaged from the source folder" ); + + try + { + JarArchiver archiver = context.getJarArchiver(); + archiver.addDirectory( source ); + archiver.setDestFile( destination ); + archiver.createArchive(); + } + catch ( ArchiverException e ) + { + String msg = "Failed to create " + targetFilename; + context.getLog().error( msg, e ); + IOException ioe = new IOException( msg ); + ioe.initCause( e ); + throw ioe; + } + } + else + { + FileUtils.copyFile( source.getCanonicalFile(), destination ); + // preserve timestamp + destination.setLastModified( source.lastModified() ); + context.getLog().debug( " + " + targetFilename + " has been copied." ); + } + return true; + } + } + + /** + * Get the encoding from an XML-file. + * + * @param webXml the XML-file + * @return The encoding of the XML-file, or UTF-8 if it's not specified in the file + * @throws java.io.IOException if an error occurred while reading the file + */ + protected String getEncoding( File webXml ) + throws IOException + { + try ( XmlStreamReader xmlReader = new XmlStreamReader( webXml ) ) + { + return xmlReader.getEncoding(); + } + } + + /** + * Returns the file to copy. If the includes are null or empty, the default includes are used. + * + * @param baseDir the base directory to start from + * @param includes the includes + * @param excludes the excludes + * @return the files to copy + */ + protected PathSet getFilesToIncludes( File baseDir, String[] includes, String[] excludes ) + { + return getFilesToIncludes( baseDir, includes, excludes, false ); + } + + /** + * Returns the file to copy. If the includes are null or empty, the default includes are used. + * + * @param baseDir the base directory to start from + * @param includes the includes + * @param excludes the excludes + * @param includeDirectories include directories yes or not. + * @return the files to copy + */ + // CHECKSTYLE_OFF: LineLength + protected PathSet getFilesToIncludes( File baseDir, String[] includes, String[] excludes, boolean includeDirectories ) + // CHECKSTYLE_ON: LineLength + { + final DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( baseDir ); + + if ( excludes != null ) + { + scanner.setExcludes( excludes ); + } + scanner.addDefaultExcludes(); + + if ( includes != null && includes.length > 0 ) + { + scanner.setIncludes( includes ); + } + else + { + scanner.setIncludes( DEFAULT_INCLUDES ); + } + + scanner.scan(); + + PathSet pathSet = new PathSet( scanner.getIncludedFiles() ); + + if ( includeDirectories ) + { + pathSet.addAll( scanner.getIncludedDirectories() ); + } + + return pathSet; + } + + /** + * Returns the final name of the specified artifact. + * + * If the outputFileNameMapping is set, it is used, otherwise the standard naming scheme is used. + * + * @param context the packaging context + * @param artifact the artifact + * @return the converted filename of the artifact + * @throws InterpolationException in case of interpolation problem. + */ + protected String getArtifactFinalName( WarPackagingContext context, Artifact artifact ) + throws InterpolationException + { + if ( context.getOutputFileNameMapping() != null ) + { + return MappingUtils.evaluateFileNameMapping( context.getOutputFileNameMapping(), artifact ); + } + + String classifier = artifact.getClassifier(); + if ( ( classifier != null ) && !( "".equals( classifier.trim() ) ) ) + { + return MappingUtils.evaluateFileNameMapping( MappingUtils.DEFAULT_FILE_NAME_MAPPING_CLASSIFIER, artifact ); + } + else + { + return MappingUtils.evaluateFileNameMapping( MappingUtils.DEFAULT_FILE_NAME_MAPPING, artifact ); + } + + } + + /** + * Returns true if the File-object is a file (not a directory) that is not + * null and has a file name that ends in ".xml". + * + * @param file The file to check + * @return true if the file is an xml-file, otherwise false + * @since 2.3 + */ + private boolean isXmlFile( File file ) + { + return file != null && file.isFile() && file.getName().endsWith( ".xml" ); + } +} diff --git a/Java/maven-war-plugin-AbstractWarPackagingTask_99/metadata.json b/Java/maven-war-plugin-AbstractWarPackagingTask_99/metadata.json new file mode 100644 index 000000000..57f0b37ab --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarPackagingTask_99/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-AbstractWarPackagingTask_99", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/AbstractWarPackagingTask.java", + "line": 123, + "npe_method": "copyFiles", + "deref_field": "targetPrefix", + "npe_class": "AbstractWarPackagingTask", + "repo": "maven-war-plugin", + "bug_id": "AbstractWarPackagingTask_99" + } +} diff --git a/Java/maven-war-plugin-AbstractWarPackagingTask_99/npe.json b/Java/maven-war-plugin-AbstractWarPackagingTask_99/npe.json new file mode 100644 index 000000000..cdcca5246 --- /dev/null +++ b/Java/maven-war-plugin-AbstractWarPackagingTask_99/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/AbstractWarPackagingTask.java", + "line": 123, + "npe_method": "copyFiles", + "deref_field": "targetPrefix", + "npe_class": "AbstractWarPackagingTask" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-CopyUserManifestTask_59/Dockerfile b/Java/maven-war-plugin-CopyUserManifestTask_59/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-CopyUserManifestTask_59/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-CopyUserManifestTask_59/buggy.java b/Java/maven-war-plugin-CopyUserManifestTask_59/buggy.java new file mode 100644 index 000000000..35832211c --- /dev/null +++ b/Java/maven-war-plugin-CopyUserManifestTask_59/buggy.java @@ -0,0 +1,69 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.logging.SystemStreamLog; + +/** + * @author Haikal Saadh + * + */ +public class CopyUserManifestTask + extends AbstractWarPackagingTask +{ + + /** Instance logger */ + private Log log; + + public Log getLog() + { + if ( log == null ) + { + log = new SystemStreamLog(); + } + return log; + } + + public void setLog( Log log ) + { + this.log = log; + } + +public void performPackaging(org.apache.maven.plugins.war.packaging.WarPackagingContext context) throws org.apache.maven.plugin.MojoExecutionException, org.apache.maven.plugin.MojoFailureException { + java.io.File userManifest = context.getArchive().getManifestFile(); + { + try { + getLog().info("Copying manifest..."); + java.io.File metainfDir = new java.io.File(context.getWebappDirectory(), org.apache.maven.plugins.war.packaging.AbstractWarPackagingTask.META_INF_PATH); + copyFile(context, /* NPEX_NULL_EXP */ + userManifest, new java.io.File(metainfDir, "MANIFEST.MF"), "META-INF/MANIFEST.MF", true); + } catch (java.io.IOException e) { + throw new org.apache.maven.plugin.MojoExecutionException("Error copying user manifest", e); + } + } +} + +} diff --git a/Java/maven-war-plugin-CopyUserManifestTask_59/metadata.json b/Java/maven-war-plugin-CopyUserManifestTask_59/metadata.json new file mode 100644 index 000000000..76d37b651 --- /dev/null +++ b/Java/maven-war-plugin-CopyUserManifestTask_59/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-CopyUserManifestTask_59", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/CopyUserManifestTask.java", + "line": 62, + "npe_method": "performPackaging", + "deref_field": "userManifest", + "npe_class": "CopyUserManifestTask", + "repo": "maven-war-plugin", + "bug_id": "CopyUserManifestTask_59" + } +} diff --git a/Java/maven-war-plugin-CopyUserManifestTask_59/npe.json b/Java/maven-war-plugin-CopyUserManifestTask_59/npe.json new file mode 100644 index 000000000..644004bce --- /dev/null +++ b/Java/maven-war-plugin-CopyUserManifestTask_59/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/CopyUserManifestTask.java", + "line": 62, + "npe_method": "performPackaging", + "deref_field": "userManifest", + "npe_class": "CopyUserManifestTask" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-JarArtifactStub_135/Dockerfile b/Java/maven-war-plugin-JarArtifactStub_135/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_135/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-JarArtifactStub_135/buggy.java b/Java/maven-war-plugin-JarArtifactStub_135/buggy.java new file mode 100644 index 000000000..399bb7196 --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_135/buggy.java @@ -0,0 +1,149 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; + +import java.io.File; + +public class JarArtifactStub + extends AbstractArtifactStub +{ + + protected String groupId; + + protected String artifactId; + + protected String version; + + protected boolean optional = false; + + protected String scope; + + private File file; + + private ArtifactHandler artifactHandler; + + public JarArtifactStub( String basedir, ArtifactHandler artifactHandler ) + { + super( basedir ); + this.artifactHandler = artifactHandler; + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.jar"; + } + } + + public String getType() + { + return "jar"; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + + public String getArtifactId() + { + if ( artifactId != null ) + { + return artifactId; + } + else + { + return "jarartifact"; + } + } + + public String getVersion() + { + if ( version != null ) + { + return version; + } + else + { + return super.getVersion(); + } + } + + public void setVersion( String version ) + { + this.version = version; + } + + public boolean isOptional() + { + return optional; + } + + public void setOptional( boolean optional ) + { + this.optional = optional; + } + + public String getScope() + { + if ( scope != null ) + { + return scope; + } + else + { + return super.getScope(); + } + } + + public void setScope( String scope ) + { + this.scope = scope; + } + +public java.io.File getFile() { + { + return /* NPEX_NULL_EXP */ + file; + } +} + + public void setFile( File file ) + { + this.file = file; + } + + public ArtifactHandler getArtifactHandler() + { + return artifactHandler; + } +} diff --git a/Java/maven-war-plugin-JarArtifactStub_135/metadata.json b/Java/maven-war-plugin-JarArtifactStub_135/metadata.json new file mode 100644 index 000000000..de8c9059c --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_135/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-JarArtifactStub_135", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java", + "line": 136, + "npe_method": "getFile", + "deref_field": "file", + "npe_class": "JarArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "JarArtifactStub_135" + } +} diff --git a/Java/maven-war-plugin-JarArtifactStub_135/npe.json b/Java/maven-war-plugin-JarArtifactStub_135/npe.json new file mode 100644 index 000000000..39921ad3a --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_135/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java", + "line": 136, + "npe_method": "getFile", + "deref_field": "file", + "npe_class": "JarArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-JarArtifactStub_79/Dockerfile b/Java/maven-war-plugin-JarArtifactStub_79/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_79/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-JarArtifactStub_79/buggy.java b/Java/maven-war-plugin-JarArtifactStub_79/buggy.java new file mode 100644 index 000000000..4b4301a60 --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_79/buggy.java @@ -0,0 +1,146 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; + +import java.io.File; + +public class JarArtifactStub + extends AbstractArtifactStub +{ + + protected String groupId; + + protected String artifactId; + + protected String version; + + protected boolean optional = false; + + protected String scope; + + private File file; + + private ArtifactHandler artifactHandler; + + public JarArtifactStub( String basedir, ArtifactHandler artifactHandler ) + { + super( basedir ); + this.artifactHandler = artifactHandler; + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.jar"; + } + } + + public String getType() + { + return "jar"; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + +public java.lang.String getArtifactId() { + { + return /* NPEX_NULL_EXP */ + artifactId; + } +} + + public String getVersion() + { + if ( version != null ) + { + return version; + } + else + { + return super.getVersion(); + } + } + + public void setVersion( String version ) + { + this.version = version; + } + + public boolean isOptional() + { + return optional; + } + + public void setOptional( boolean optional ) + { + this.optional = optional; + } + + public String getScope() + { + if ( scope != null ) + { + return scope; + } + else + { + return super.getScope(); + } + } + + public void setScope( String scope ) + { + this.scope = scope; + } + + public File getFile() + { + if ( file == null ) + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.jar" ); + } + return file; + } + + public void setFile( File file ) + { + this.file = file; + } + + public ArtifactHandler getArtifactHandler() + { + return artifactHandler; + } +} diff --git a/Java/maven-war-plugin-JarArtifactStub_79/metadata.json b/Java/maven-war-plugin-JarArtifactStub_79/metadata.json new file mode 100644 index 000000000..6d554e08a --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_79/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-JarArtifactStub_79", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java", + "line": 80, + "npe_method": "getArtifactId", + "deref_field": "artifactId", + "npe_class": "JarArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "JarArtifactStub_79" + } +} diff --git a/Java/maven-war-plugin-JarArtifactStub_79/npe.json b/Java/maven-war-plugin-JarArtifactStub_79/npe.json new file mode 100644 index 000000000..3506296b7 --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_79/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java", + "line": 80, + "npe_method": "getArtifactId", + "deref_field": "artifactId", + "npe_class": "JarArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-JarArtifactStub_91/Dockerfile b/Java/maven-war-plugin-JarArtifactStub_91/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_91/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-JarArtifactStub_91/buggy.java b/Java/maven-war-plugin-JarArtifactStub_91/buggy.java new file mode 100644 index 000000000..31d296673 --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_91/buggy.java @@ -0,0 +1,146 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.artifact.handler.ArtifactHandler; + +import java.io.File; + +public class JarArtifactStub + extends AbstractArtifactStub +{ + + protected String groupId; + + protected String artifactId; + + protected String version; + + protected boolean optional = false; + + protected String scope; + + private File file; + + private ArtifactHandler artifactHandler; + + public JarArtifactStub( String basedir, ArtifactHandler artifactHandler ) + { + super( basedir ); + this.artifactHandler = artifactHandler; + } + + public void setGroupId( String id ) + { + groupId = id; + } + + public String getGroupId() + { + if ( groupId != null ) + { + return groupId; + } + else + { + return "org.sample.jar"; + } + } + + public String getType() + { + return "jar"; + } + + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + + public String getArtifactId() + { + if ( artifactId != null ) + { + return artifactId; + } + else + { + return "jarartifact"; + } + } + +public java.lang.String getVersion() { + { + return /* NPEX_NULL_EXP */ + version; + } +} + + public void setVersion( String version ) + { + this.version = version; + } + + public boolean isOptional() + { + return optional; + } + + public void setOptional( boolean optional ) + { + this.optional = optional; + } + + public String getScope() + { + if ( scope != null ) + { + return scope; + } + else + { + return super.getScope(); + } + } + + public void setScope( String scope ) + { + this.scope = scope; + } + + public File getFile() + { + if ( file == null ) + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.jar" ); + } + return file; + } + + public void setFile( File file ) + { + this.file = file; + } + + public ArtifactHandler getArtifactHandler() + { + return artifactHandler; + } +} diff --git a/Java/maven-war-plugin-JarArtifactStub_91/metadata.json b/Java/maven-war-plugin-JarArtifactStub_91/metadata.json new file mode 100644 index 000000000..6ad7f1048 --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_91/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-JarArtifactStub_91", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java", + "line": 92, + "npe_method": "getVersion", + "deref_field": "version", + "npe_class": "JarArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "JarArtifactStub_91" + } +} diff --git a/Java/maven-war-plugin-JarArtifactStub_91/npe.json b/Java/maven-war-plugin-JarArtifactStub_91/npe.json new file mode 100644 index 000000000..858b5dc7c --- /dev/null +++ b/Java/maven-war-plugin-JarArtifactStub_91/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/JarArtifactStub.java", + "line": 92, + "npe_method": "getVersion", + "deref_field": "version", + "npe_class": "JarArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-OverlayManager_200/Dockerfile b/Java/maven-war-plugin-OverlayManager_200/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-OverlayManager_200/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-OverlayManager_200/buggy.java b/Java/maven-war-plugin-OverlayManager_200/buggy.java new file mode 100644 index 000000000..238828566 --- /dev/null +++ b/Java/maven-war-plugin-OverlayManager_200/buggy.java @@ -0,0 +1,256 @@ +package org.apache.maven.plugins.war.overlay; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; +import java.util.Set; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.project.MavenProject; + +/** + * Manages the overlays. + * + * @author Stephane Nicoll + */ +public class OverlayManager +{ + private final List overlays; + + private final MavenProject project; + + private final List artifactsOverlays; + + /** + * Creates a manager with the specified overlays. + * + * Note that the list is potentially updated by the manager so a new list is created based on the overlays. + * + * @param overlays the overlays + * @param project the maven project + * @param defaultIncludes the default includes to use + * @param defaultExcludes the default excludes to use + * @param currentProjectOverlay the overlay for the current project + * @throws InvalidOverlayConfigurationException if the config is invalid + */ + public OverlayManager( List overlays, MavenProject project, String[] defaultIncludes, + String[] defaultExcludes, Overlay currentProjectOverlay ) + throws InvalidOverlayConfigurationException + { + this.overlays = new ArrayList<>(); + if ( overlays != null ) + { + this.overlays.addAll( overlays ); + } + this.project = project; + + this.artifactsOverlays = getOverlaysAsArtifacts(); + + // Initialize + initialize( defaultIncludes, defaultExcludes, currentProjectOverlay ); + + } + + /** + * Returns the resolved overlays. + * + * @return the overlays + */ + public List getOverlays() + { + return overlays; + } + + /** + * Returns the id of the resolved overlays. + * + * @return the overlay ids + */ + public List getOverlayIds() + { + final List result = new ArrayList<>(); + for ( Overlay overlay : overlays ) + { + result.add( overlay.getId() ); + } + return result; + + } + + /** + * Initializes the manager and validates the overlays configuration. + * + * @param defaultIncludes the default includes to use + * @param defaultExcludes the default excludes to use + * @param currentProjectOverlay the overlay for the current project + * @throws InvalidOverlayConfigurationException if the configuration is invalid + */ + void initialize( String[] defaultIncludes, String[] defaultExcludes, Overlay currentProjectOverlay ) + throws InvalidOverlayConfigurationException + { + + // Build the list of configured artifacts and makes sure that each overlay + // refer to a valid artifact + final List configuredWarArtifacts = new ArrayList<>(); + final ListIterator it = overlays.listIterator(); + while ( it.hasNext() ) + { + Overlay overlay = it.next(); + if ( overlay == null ) + { + throw new InvalidOverlayConfigurationException( "overlay could not be null." ); + } + // If it's the current project, return the project instance + if ( overlay.isCurrentProject() ) + { + overlay = currentProjectOverlay; + it.set( overlay ); + } + // default includes/excludes - only if the overlay uses the default settings + if ( Arrays.equals( Overlay.DEFAULT_INCLUDES, overlay.getIncludes() ) + && Arrays.equals( Overlay.DEFAULT_EXCLUDES, overlay.getExcludes() ) ) + { + overlay.setIncludes( defaultIncludes ); + overlay.setExcludes( defaultExcludes ); + } + + final Artifact artifact = getAssociatedArtifact( overlay ); + if ( artifact != null ) + { + configuredWarArtifacts.add( artifact ); + overlay.setArtifact( artifact ); + } + } + + // Build the list of missing overlays + for ( Artifact artifact : artifactsOverlays ) + { + if ( !configuredWarArtifacts.contains( artifact ) ) + { + // Add a default overlay for the given artifact which will be applied after + // the ones that have been configured + overlays.add( new DefaultOverlay( artifact, defaultIncludes, defaultExcludes ) ); + } + } + + // Final validation, make sure that the current project is in there. Otherwise add it first + for ( Overlay overlay : overlays ) + { + if ( overlay.equals( currentProjectOverlay ) ) + { + return; + } + } + overlays.add( 0, currentProjectOverlay ); + } + + /** + * Returns the Artifact associated to the specified overlay. + * + * If the overlay defines the current project, null is returned. If no artifact could not be found for the + * overlay a InvalidOverlayConfigurationException is thrown. + * + * @param overlay an overlay + * @return the artifact associated to the overlay + * @throws org.apache.maven.plugins.war.overlay.InvalidOverlayConfigurationException if the overlay does not have an + * associated artifact + */ +/** + * Returns the Artifact associated to the specified overlay. + * + * If the overlay defines the current project, null is returned. If no artifact could not be found for the + * overlay a InvalidOverlayConfigurationException is thrown. + * + * @param overlay + * an overlay + * @return the artifact associated to the overlay + * @throws org.apache.maven.plugins.war.overlay.InvalidOverlayConfigurationException + * if the overlay does not have an + * associated artifact + */ +org.apache.maven.artifact.Artifact getAssociatedArtifact(final org.apache.maven.plugins.war.Overlay overlay) throws org.apache.maven.plugins.war.overlay.InvalidOverlayConfigurationException { + if (overlay.isCurrentProject()) { + return null; + } + for (org.apache.maven.artifact.Artifact artifact : artifactsOverlays) { + // Handle classifier dependencies properly (clash management) + if (compareOverlayWithArtifact(overlay, artifact)) { + return artifact; + } + } + // maybe its a project dependencies zip or an other type + java.util.Set projectArtifacts = this.project.getDependencyArtifacts(); + { + for (org.apache.maven.artifact.Artifact artifact : /* NPEX_NULL_EXP */ + projectArtifacts) { + if (compareOverlayWithArtifact(overlay, artifact)) { + return artifact; + } + } + } + // CHECKSTYLE_OFF: LineLength + throw new org.apache.maven.plugins.war.overlay.InvalidOverlayConfigurationException(("overlay [" + overlay) + "] is not a dependency of the project."); + // CHECKSTYLE_ON: LineLength +} + + /** + * Compare groupId && artifactId && type && classifier. + * + * @param overlay the overlay + * @param artifact the artifact + * @return boolean true if equals + */ + private boolean compareOverlayWithArtifact( Overlay overlay, Artifact artifact ) + { + return ( Objects.equals( overlay.getGroupId(), artifact.getGroupId() ) + && Objects.equals( overlay.getArtifactId(), artifact.getArtifactId() ) + && Objects.equals( overlay.getType(), artifact.getType() ) + // MWAR-241 Make sure to treat null and "" as equal when comparing the classifier + && Objects.equals( Objects.toString( overlay.getClassifier() ), + Objects.toString( artifact.getClassifier() ) ) ); + } + + /** + * Returns a list of WAR {@link org.apache.maven.artifact.Artifact} describing the overlays of the current project. + * + * @return the overlays as artifacts objects + */ + private List getOverlaysAsArtifacts() + { + ScopeArtifactFilter filter = new ScopeArtifactFilter( Artifact.SCOPE_RUNTIME ); + final Set artifacts = project.getArtifacts(); + + final List result = new ArrayList<>(); + for ( Artifact artifact : artifacts ) + { + if ( !artifact.isOptional() && filter.include( artifact ) && ( "war".equals( artifact.getType() ) ) ) + { + result.add( artifact ); + } + } + return result; + } +} diff --git a/Java/maven-war-plugin-OverlayManager_200/metadata.json b/Java/maven-war-plugin-OverlayManager_200/metadata.json new file mode 100644 index 000000000..119289202 --- /dev/null +++ b/Java/maven-war-plugin-OverlayManager_200/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-OverlayManager_200", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/plugins/war/overlay/OverlayManager.java", + "line": 208, + "npe_method": "getAssociatedArtifact", + "deref_field": "projectArtifacts", + "npe_class": "OverlayManager", + "repo": "maven-war-plugin", + "bug_id": "OverlayManager_200" + } +} diff --git a/Java/maven-war-plugin-OverlayManager_200/npe.json b/Java/maven-war-plugin-OverlayManager_200/npe.json new file mode 100644 index 000000000..28847e1d7 --- /dev/null +++ b/Java/maven-war-plugin-OverlayManager_200/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/plugins/war/overlay/OverlayManager.java", + "line": 208, + "npe_method": "getAssociatedArtifact", + "deref_field": "projectArtifacts", + "npe_class": "OverlayManager" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-OverlayPackagingTask_80/Dockerfile b/Java/maven-war-plugin-OverlayPackagingTask_80/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-OverlayPackagingTask_80/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-OverlayPackagingTask_80/buggy.java b/Java/maven-war-plugin-OverlayPackagingTask_80/buggy.java new file mode 100644 index 000000000..1c25b794b --- /dev/null +++ b/Java/maven-war-plugin-OverlayPackagingTask_80/buggy.java @@ -0,0 +1,139 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.plugins.war.util.PathSet; +import org.codehaus.plexus.util.FileUtils; + +import java.io.File; +import java.io.IOException; + +/** + * Handles an overlay. + * + * @author Stephane Nicoll + */ +public class OverlayPackagingTask + extends AbstractWarPackagingTask +{ + private final Overlay overlay; + + /** + * @param overlay {@link #overlay} + * @param currentProjectOverlay current overlay. + */ + public OverlayPackagingTask( Overlay overlay, Overlay currentProjectOverlay ) + { + if ( overlay == null ) + { + throw new NullPointerException( "overlay could not be null." ); + } + if ( overlay.equals( currentProjectOverlay ) ) + { + throw new IllegalStateException( "Could not handle the current project with this task." ); + } + this.overlay = overlay; + } + + @Override +public void performPackaging(org.apache.maven.plugins.war.packaging.WarPackagingContext context) throws org.apache.maven.plugin.MojoExecutionException { + context.getLog().debug("OverlayPackagingTask performPackaging overlay.getTargetPath() " + overlay.getTargetPath()); + if (overlay.shouldSkip()) { + context.getLog().info(("Skipping overlay [" + overlay) + "]"); + } else { + try { + context.getLog().info(("Processing overlay [" + overlay) + "]"); + // Step1: Extract if necessary + final java.io.File tmpDir = unpackOverlay(context, overlay); + // Step2: setup + final org.apache.maven.plugins.war.util.PathSet includes = getFilesToIncludes(tmpDir, overlay.getIncludes(), overlay.getExcludes()); + { + // overlay.getTargetPath() must ended with / + // if not we add it + java.lang.String targetPath = /* NPEX_NULL_EXP */ + overlay.getTargetPath(); + if (!targetPath.endsWith("/")) { + targetPath = targetPath + "/"; + } + copyFiles(overlay.getId(), context, tmpDir, includes, targetPath, overlay.isFiltered()); + } + } catch (java.io.IOException e) { + throw new org.apache.maven.plugin.MojoExecutionException(("Failed to copy file for overlay [" + overlay) + "]", e); + } + } +} + + /** + * Unpacks the specified overlay. + * + * Makes sure to skip the unpack process if the overlay has already been unpacked. + * + * @param context the packaging context + * @param overlay the overlay + * @return the directory containing the unpacked overlay + * @throws MojoExecutionException if an error occurred while unpacking the overlay + */ + protected File unpackOverlay( WarPackagingContext context, Overlay overlay ) + throws MojoExecutionException + { + final File tmpDir = getOverlayTempDirectory( context, overlay ); + + // TODO: not sure it's good, we should reuse the markers of the dependency plugin + if ( FileUtils.sizeOfDirectory( tmpDir ) == 0 + || overlay.getArtifact().getFile().lastModified() > tmpDir.lastModified() ) + { + doUnpack( context, overlay.getArtifact().getFile(), tmpDir ); + } + else + { + context.getLog().debug( "Overlay [" + overlay + "] was already unpacked" ); + } + return tmpDir; + } + + /** + * Returns the directory to use to unpack the specified overlay. + * + * @param context the packaging context + * @param overlay the overlay + * @return the temp directory for the overlay + */ + protected File getOverlayTempDirectory( WarPackagingContext context, Overlay overlay ) + { + final File groupIdDir = new File( context.getOverlaysWorkDirectory(), overlay.getGroupId() ); + if ( !groupIdDir.exists() ) + { + groupIdDir.mkdir(); + } + String directoryName = overlay.getArtifactId(); + if ( overlay.getClassifier() != null ) + { + directoryName = directoryName + "-" + overlay.getClassifier(); + } + final File result = new File( groupIdDir, directoryName ); + if ( !result.exists() ) + { + result.mkdirs(); + } + return result; + } +} diff --git a/Java/maven-war-plugin-OverlayPackagingTask_80/metadata.json b/Java/maven-war-plugin-OverlayPackagingTask_80/metadata.json new file mode 100644 index 000000000..4022306d1 --- /dev/null +++ b/Java/maven-war-plugin-OverlayPackagingTask_80/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-OverlayPackagingTask_80", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/OverlayPackagingTask.java", + "line": 73, + "npe_method": "performPackaging", + "deref_field": "getTargetPath", + "npe_class": "OverlayPackagingTask", + "repo": "maven-war-plugin", + "bug_id": "OverlayPackagingTask_80" + } +} diff --git a/Java/maven-war-plugin-OverlayPackagingTask_80/npe.json b/Java/maven-war-plugin-OverlayPackagingTask_80/npe.json new file mode 100644 index 000000000..8a13aedbc --- /dev/null +++ b/Java/maven-war-plugin-OverlayPackagingTask_80/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/OverlayPackagingTask.java", + "line": 73, + "npe_method": "performPackaging", + "deref_field": "getTargetPath", + "npe_class": "OverlayPackagingTask" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-WarArtifactStub_51/Dockerfile b/Java/maven-war-plugin-WarArtifactStub_51/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_51/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-WarArtifactStub_51/buggy.java b/Java/maven-war-plugin-WarArtifactStub_51/buggy.java new file mode 100644 index 000000000..3da998746 --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_51/buggy.java @@ -0,0 +1,110 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +/** + * Stub + */ +public class WarArtifactStub + extends AbstractArtifactStub +{ + + private String groupId; + + private String artifactId; + + private String classifier; + + private File file; + + public WarArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public String getType() + { + return "war"; + } + +public java.lang.String getArtifactId() { + { + return /* NPEX_NULL_EXP */ + artifactId; + } +} + + public void setArtifactId( String _artifactId ) + { + artifactId = _artifactId; + } + + + public String getGroupId() + { + if ( groupId == null ) + { + return "wartests"; + } + else + { + return groupId; + } + } + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + public File getFile() + { + if ( file == null ) + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.war" ); + } + else + { + return file; + } + } + + public void setFile( File _file ) + { + file = _file; + } + + public String getClassifier() + { + return classifier; + } + + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + public boolean hasClassifier() + { + return classifier != null; + } +} diff --git a/Java/maven-war-plugin-WarArtifactStub_51/metadata.json b/Java/maven-war-plugin-WarArtifactStub_51/metadata.json new file mode 100644 index 000000000..0ccf7a784 --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_51/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-WarArtifactStub_51", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java", + "line": 52, + "npe_method": "getArtifactId", + "deref_field": "artifactId", + "npe_class": "WarArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "WarArtifactStub_51" + } +} diff --git a/Java/maven-war-plugin-WarArtifactStub_51/npe.json b/Java/maven-war-plugin-WarArtifactStub_51/npe.json new file mode 100644 index 000000000..25554f83c --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_51/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java", + "line": 52, + "npe_method": "getArtifactId", + "deref_field": "artifactId", + "npe_class": "WarArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-WarArtifactStub_69/Dockerfile b/Java/maven-war-plugin-WarArtifactStub_69/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_69/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-WarArtifactStub_69/buggy.java b/Java/maven-war-plugin-WarArtifactStub_69/buggy.java new file mode 100644 index 000000000..b1e2c571b --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_69/buggy.java @@ -0,0 +1,110 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +/** + * Stub + */ +public class WarArtifactStub + extends AbstractArtifactStub +{ + + private String groupId; + + private String artifactId; + + private String classifier; + + private File file; + + public WarArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public String getType() + { + return "war"; + } + + public String getArtifactId() + { + if ( artifactId == null ) + { + return "simple"; + } + else + { + return artifactId; + } + } + + public void setArtifactId( String _artifactId ) + { + artifactId = _artifactId; + } + + +public java.lang.String getGroupId() { + { + return /* NPEX_NULL_EXP */ + groupId; + } +} + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + + public File getFile() + { + if ( file == null ) + { + return new File( basedir, "/target/test-classes/unit/sample_wars/simple.war" ); + } + else + { + return file; + } + } + + public void setFile( File _file ) + { + file = _file; + } + + public String getClassifier() + { + return classifier; + } + + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + public boolean hasClassifier() + { + return classifier != null; + } +} diff --git a/Java/maven-war-plugin-WarArtifactStub_69/metadata.json b/Java/maven-war-plugin-WarArtifactStub_69/metadata.json new file mode 100644 index 000000000..b42544325 --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_69/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-WarArtifactStub_69", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java", + "line": 70, + "npe_method": "getGroupId", + "deref_field": "groupId", + "npe_class": "WarArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "WarArtifactStub_69" + } +} diff --git a/Java/maven-war-plugin-WarArtifactStub_69/npe.json b/Java/maven-war-plugin-WarArtifactStub_69/npe.json new file mode 100644 index 000000000..a4fdcaaf8 --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_69/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java", + "line": 70, + "npe_method": "getGroupId", + "deref_field": "groupId", + "npe_class": "WarArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-WarArtifactStub_86/Dockerfile b/Java/maven-war-plugin-WarArtifactStub_86/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_86/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-WarArtifactStub_86/buggy.java b/Java/maven-war-plugin-WarArtifactStub_86/buggy.java new file mode 100644 index 000000000..d321ef40e --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_86/buggy.java @@ -0,0 +1,110 @@ +package org.apache.maven.plugins.war.stub; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; + +/** + * Stub + */ +public class WarArtifactStub + extends AbstractArtifactStub +{ + + private String groupId; + + private String artifactId; + + private String classifier; + + private File file; + + public WarArtifactStub( String _basedir ) + { + super( _basedir ); + } + + public String getType() + { + return "war"; + } + + public String getArtifactId() + { + if ( artifactId == null ) + { + return "simple"; + } + else + { + return artifactId; + } + } + + public void setArtifactId( String _artifactId ) + { + artifactId = _artifactId; + } + + + public String getGroupId() + { + if ( groupId == null ) + { + return "wartests"; + } + else + { + return groupId; + } + } + + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + +public java.io.File getFile() { + { + return /* NPEX_NULL_EXP */ + file; + } +} + + public void setFile( File _file ) + { + file = _file; + } + + public String getClassifier() + { + return classifier; + } + + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + public boolean hasClassifier() + { + return classifier != null; + } +} diff --git a/Java/maven-war-plugin-WarArtifactStub_86/metadata.json b/Java/maven-war-plugin-WarArtifactStub_86/metadata.json new file mode 100644 index 000000000..e2d5de83a --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_86/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-WarArtifactStub_86", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java", + "line": 87, + "npe_method": "getFile", + "deref_field": "file", + "npe_class": "WarArtifactStub", + "repo": "maven-war-plugin", + "bug_id": "WarArtifactStub_86" + } +} diff --git a/Java/maven-war-plugin-WarArtifactStub_86/npe.json b/Java/maven-war-plugin-WarArtifactStub_86/npe.json new file mode 100644 index 000000000..f20ce5359 --- /dev/null +++ b/Java/maven-war-plugin-WarArtifactStub_86/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/test/java/org/apache/maven/plugins/war/stub/WarArtifactStub.java", + "line": 87, + "npe_method": "getFile", + "deref_field": "file", + "npe_class": "WarArtifactStub" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-WarMojo_349/Dockerfile b/Java/maven-war-plugin-WarMojo_349/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-WarMojo_349/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-WarMojo_349/buggy.java b/Java/maven-war-plugin-WarMojo_349/buggy.java new file mode 100644 index 000000000..7b67e1b04 --- /dev/null +++ b/Java/maven-war-plugin-WarMojo_349/buggy.java @@ -0,0 +1,583 @@ +package org.apache.maven.plugins.war; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.archiver.MavenArchiver; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.plugins.war.util.ClassesPackager; +import org.apache.maven.project.MavenProjectHelper; +import org.codehaus.plexus.archiver.Archiver; +import org.codehaus.plexus.archiver.ArchiverException; +import org.codehaus.plexus.archiver.jar.ManifestException; +import org.codehaus.plexus.archiver.war.WarArchiver; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.StringUtils; + +/** + * Build a WAR file. + * + * @author Emmanuel Venisse + */ +@Mojo( name = "war", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME ) +public class WarMojo + extends AbstractWarMojo +{ + /** + * The directory for the generated WAR. + */ + @Parameter( defaultValue = "${project.build.directory}", required = true ) + private String outputDirectory; + + /** + * The name of the generated WAR. + */ + @Parameter( defaultValue = "${project.build.finalName}", required = true, readonly = true ) + private String warName; + + /** + * Classifier to add to the generated WAR. If given, the artifact will be an attachment instead. The classifier will + * not be applied to the JAR file of the project - only to the WAR file. + */ + @Parameter + private String classifier; + + /** + * The comma separated list of tokens to exclude from the WAR before packaging. This option may be used to implement + * the skinny WAR use case. Note that you can use the Java Regular Expressions engine to include and exclude + * specific pattern using the expression %regex[]. Hint: read the about (?!Pattern). + * + * @since 2.1-alpha-2 + */ + @Parameter + private String packagingExcludes; + + /** + * The comma separated list of tokens to include in the WAR before packaging. By default everything is included. + * This option may be used to implement the skinny WAR use case. Note that you can use the Java Regular Expressions + * engine to include and exclude specific pattern using the expression %regex[]. + * + * @since 2.1-beta-1 + */ + @Parameter + private String packagingIncludes; + + /** + * The WAR archiver. + */ + @Component( role = Archiver.class, hint = "war" ) + private WarArchiver warArchiver; + + /** + */ + @Component + private MavenProjectHelper projectHelper; + + /** + * Whether this is the main artifact being built. Set to false if you don't want to install or deploy + * it to the local repository instead of the default one in an execution. + */ + @Parameter( defaultValue = "true" ) + private boolean primaryArtifact; + + /** + * Whether classes (that is the content of the WEB-INF/classes directory) should be attached to the project as an + * additional artifact. + *

+ * By default the classifier for the additional artifact is 'classes'. You can change it with the + * someclassifier]]> parameter. + *

+ *

+ * If this parameter true, another project can depend on the classes by writing something like: + * + *

+     * 
+     *   myGroup
+     *   myArtifact
+     *   myVersion
+     *   classes
+     * ]]>
+     * 
+ *

+ * + * @since 2.1-alpha-2 + */ + @Parameter( defaultValue = "false" ) + private boolean attachClasses; + + /** + * The classifier to use for the attached classes artifact. + * + * @since 2.1-alpha-2 + */ + @Parameter( defaultValue = "classes" ) + private String classesClassifier; + + /** + * You can skip the execution of the plugin if you need to. Its use is NOT RECOMMENDED, but quite convenient on + * occasion. + * + * @since 3.0.0 + */ + @Parameter( property = "maven.war.skip", defaultValue = "false" ) + private boolean skip; + + // ---------------------------------------------------------------------- + // Implementation + // ---------------------------------------------------------------------- + + /** + * Executes the WarMojo on the current project. + * + * @throws MojoExecutionException if an error occurred while building the webapp + * @throws MojoFailureException if an error. + */ + @Override + public void execute() + throws MojoExecutionException, MojoFailureException + { + + if ( isSkip() ) + { + getLog().info( "Skipping the execution." ); + return; + } + + File warFile = getTargetWarFile(); + + try + { + performPackaging( warFile ); + } + catch ( DependencyResolutionRequiredException | ArchiverException e ) + { + throw new MojoExecutionException( "Error assembling WAR: " + e.getMessage(), e ); + } + catch ( ManifestException | IOException e ) + { + throw new MojoExecutionException( "Error assembling WAR", e ); + } + } + + /** + * Generates the webapp according to the mode attribute. + * + * @param warFile the target WAR file + * @throws IOException if an error occurred while copying files + * @throws ArchiverException if the archive could not be created + * @throws ManifestException if the manifest could not be created + * @throws DependencyResolutionRequiredException if an error occurred while resolving the dependencies + * @throws MojoExecutionException if the execution failed + * @throws MojoFailureException if a fatal exception occurred + */ + private void performPackaging( File warFile ) + throws IOException, ManifestException, DependencyResolutionRequiredException, MojoExecutionException, + MojoFailureException + { + getLog().info( "Packaging webapp" ); + + buildExplodedWebapp( getWebappDirectory() ); + + MavenArchiver archiver = new MavenArchiver(); + + archiver.setArchiver( warArchiver ); + + archiver.setCreatedBy( "Maven WAR Plugin", "org.apache.maven.plugins", "maven-war-plugin" ); + + archiver.setOutputFile( warFile ); + + // configure for Reproducible Builds based on outputTimestamp value + archiver.configureReproducible( outputTimestamp ); + + getLog().debug( "Excluding " + Arrays.asList( getPackagingExcludes() ) + + " from the generated webapp archive." ); + getLog().debug( "Including " + Arrays.asList( getPackagingIncludes() ) + " in the generated webapp archive." ); + + warArchiver.addDirectory( getWebappDirectory(), getPackagingIncludes(), getPackagingExcludes() ); + + final File webXmlFile = new File( getWebappDirectory(), "WEB-INF/web.xml" ); + if ( webXmlFile.exists() ) + { + warArchiver.setWebxml( webXmlFile ); + } + + warArchiver.setRecompressAddedZips( isRecompressZippedFiles() ); + + warArchiver.setIncludeEmptyDirs( isIncludeEmptyDirectories() ); + + if ( Boolean.FALSE.equals( failOnMissingWebXml ) + || ( failOnMissingWebXml == null && isProjectUsingAtLeastServlet30() ) ) + { + getLog().debug( "Build won't fail if web.xml file is missing." ); + warArchiver.setExpectWebXml( false ); + } + + // create archive + archiver.createArchive( getSession(), getProject(), getArchive() ); + + // create the classes to be attached if necessary + if ( isAttachClasses() ) + { + if ( isArchiveClasses() && getJarArchiver().getDestFile() != null ) + { + // special handling in case of archived classes: MWAR-240 + File targetClassesFile = getTargetClassesFile(); + FileUtils.copyFile( getJarArchiver().getDestFile(), targetClassesFile ); + projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), targetClassesFile ); + } + else + { + ClassesPackager packager = new ClassesPackager(); + final File classesDirectory = packager.getClassesDirectory( getWebappDirectory() ); + if ( classesDirectory.exists() ) + { + getLog().info( "Packaging classes" ); + packager.packageClasses( classesDirectory, getTargetClassesFile(), getJarArchiver(), getSession(), + getProject(), getArchive(), outputTimestamp ); + projectHelper.attachArtifact( getProject(), "jar", getClassesClassifier(), getTargetClassesFile() ); + } + } + } + + if ( this.classifier != null ) + { + projectHelper.attachArtifact( getProject(), "war", this.classifier, warFile ); + } + else + { + Artifact artifact = getProject().getArtifact(); + if ( primaryArtifact ) + { + artifact.setFile( warFile ); + } + else if ( artifact.getFile() == null || artifact.getFile().isDirectory() ) + { + artifact.setFile( warFile ); + } + } + } + + /** + * Determines if the current Maven project being built uses the Servlet 3.0 API (JSR 315) + * or Jakarta Servlet API. + * If it does then the web.xml file can be omitted. + *

+ * This is done by checking if the interface javax.servlet.annotation.WebServlet + * or jakarta.servlet.annotation.WebServlet is in the compile-time + * dependencies (which includes provided dependencies) of the Maven project. + * + * @return true if the project being built depends on Servlet 3.0 API or Jakarta Servlet API, + * false otherwise. + * @throws DependencyResolutionRequiredException if the compile elements can't be resolved. + * @throws MalformedURLException if the path to a dependency file can't be transformed to a URL. + */ + private boolean isProjectUsingAtLeastServlet30() + throws DependencyResolutionRequiredException, MalformedURLException + { + List classpathElements = getProject().getCompileClasspathElements(); + URL[] urls = new URL[classpathElements.size()]; + for ( int i = 0; i < urls.length; i++ ) + { + urls[i] = new File( classpathElements.get( i ) ).toURI().toURL(); + } + ClassLoader loader = new URLClassLoader( urls, Thread.currentThread().getContextClassLoader() ); + + return hasWebServletAnnotationClassInClasspath( loader ); + } + + private static boolean hasWebServletAnnotationClassInClasspath( ClassLoader loader ) + { + return hasClassInClasspath( loader, "javax.servlet.annotation.WebServlet" ) + || hasClassInClasspath( loader, "jakarta.servlet.annotation.WebServlet" ); + } + + private static boolean hasClassInClasspath( ClassLoader loader, String clazz ) + { + try + { + Class.forName( clazz, false, loader ); + return true; + } + catch ( ClassNotFoundException e ) + { + return false; + } + } + + /** + * @param basedir The basedir + * @param finalName The finalName + * @param classifier The classifier. + * @param type The type. + * @return {@link File} + */ +/** + * + * + * @param basedir + * The basedir + * @param finalName + * The finalName + * @param classifier + * The classifier. + * @param type + * The type. + * @return {@link File} + */ +protected static java.io.File getTargetFile(java.io.File basedir, java.lang.String finalName, java.lang.String classifier, java.lang.String type) { + { + if ((/* NPEX_NULL_EXP */ + classifier.trim().length() > 0) && (!classifier.startsWith("-"))) { + classifier = "-" + classifier; + } + } + return new java.io.File(basedir, ((finalName + classifier) + ".") + type); +} + + /** + * @return The war {@link File} + */ + protected File getTargetWarFile() + { + return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassifier(), "war" ); + + } + + /** + * @return The target class {@link File} + */ + protected File getTargetClassesFile() + { + return getTargetFile( new File( getOutputDirectory() ), getWarName(), getClassesClassifier(), "jar" ); + } + + // Getters and Setters + + /** + * @return {@link #classifier} + */ + public String getClassifier() + { + return classifier; + } + + /** + * @param classifier {@link #classifier} + */ + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + + /** + * @return The package excludes. + */ + public String[] getPackagingExcludes() + { + if ( StringUtils.isEmpty( packagingExcludes ) ) + { + return new String[0]; + } + else + { + return StringUtils.split( packagingExcludes, "," ); + } + } + + /** + * @param packagingExcludes {@link #packagingExcludes} + */ + public void setPackagingExcludes( String packagingExcludes ) + { + this.packagingExcludes = packagingExcludes; + } + + /** + * @return The packaging includes. + */ + public String[] getPackagingIncludes() + { + if ( StringUtils.isEmpty( packagingIncludes ) ) + { + return new String[] { "**" }; + } + else + { + return StringUtils.split( packagingIncludes, "," ); + } + } + + /** + * @param packagingIncludes {@link #packagingIncludes} + */ + public void setPackagingIncludes( String packagingIncludes ) + { + this.packagingIncludes = packagingIncludes; + } + + /** + * @return {@link #outputDirectory} + */ + public String getOutputDirectory() + { + return outputDirectory; + } + + /** + * @param outputDirectory {@link #outputDirectory} + */ + public void setOutputDirectory( String outputDirectory ) + { + this.outputDirectory = outputDirectory; + } + + /** + * @return {@link #warName} + */ + public String getWarName() + { + return warName; + } + + /** + * @param warName {@link #warName} + */ + public void setWarName( String warName ) + { + this.warName = warName; + } + + /** + * @return {@link #warArchiver} + */ + public WarArchiver getWarArchiver() + { + return warArchiver; + } + + /** + * @param warArchiver {@link #warArchiver} + */ + public void setWarArchiver( WarArchiver warArchiver ) + { + this.warArchiver = warArchiver; + } + + /** + * @return {@link #projectHelper} + */ + public MavenProjectHelper getProjectHelper() + { + return projectHelper; + } + + /** + * @param projectHelper {@link #projectHelper} + */ + public void setProjectHelper( MavenProjectHelper projectHelper ) + { + this.projectHelper = projectHelper; + } + + /** + * @return {@link #primaryArtifact} + */ + public boolean isPrimaryArtifact() + { + return primaryArtifact; + } + + /** + * @param primaryArtifact {@link #primaryArtifact} + */ + public void setPrimaryArtifact( boolean primaryArtifact ) + { + this.primaryArtifact = primaryArtifact; + } + + /** + * @return {@link #attachClasses} + */ + public boolean isAttachClasses() + { + return attachClasses; + } + + /** + * @param attachClasses {@link #attachClasses} + */ + public void setAttachClasses( boolean attachClasses ) + { + this.attachClasses = attachClasses; + } + + /** + * @return {@link #classesClassifier} + */ + public String getClassesClassifier() + { + return classesClassifier; + } + + /** + * @param classesClassifier {@link #classesClassifier} + */ + public void setClassesClassifier( String classesClassifier ) + { + this.classesClassifier = classesClassifier; + } + + /** + * @return {@link #failOnMissingWebXml} + */ + public boolean isFailOnMissingWebXml() + { + return failOnMissingWebXml; + } + + /** + * @param failOnMissingWebXml {@link #failOnMissingWebXml} + */ + public void setFailOnMissingWebXml( boolean failOnMissingWebXml ) + { + this.failOnMissingWebXml = failOnMissingWebXml; + } + + public boolean isSkip() + { + return skip; + } +} diff --git a/Java/maven-war-plugin-WarMojo_349/metadata.json b/Java/maven-war-plugin-WarMojo_349/metadata.json new file mode 100644 index 000000000..1f0939743 --- /dev/null +++ b/Java/maven-war-plugin-WarMojo_349/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-WarMojo_349", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/plugins/war/WarMojo.java", + "line": 363, + "npe_method": "getTargetFile", + "deref_field": "classifier", + "npe_class": "WarMojo", + "repo": "maven-war-plugin", + "bug_id": "WarMojo_349" + } +} diff --git a/Java/maven-war-plugin-WarMojo_349/npe.json b/Java/maven-war-plugin-WarMojo_349/npe.json new file mode 100644 index 000000000..4073aec0b --- /dev/null +++ b/Java/maven-war-plugin-WarMojo_349/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/plugins/war/WarMojo.java", + "line": 363, + "npe_method": "getTargetFile", + "deref_field": "classifier", + "npe_class": "WarMojo" +} \ No newline at end of file diff --git a/Java/maven-war-plugin-WarProjectPackagingTask_327/Dockerfile b/Java/maven-war-plugin-WarProjectPackagingTask_327/Dockerfile new file mode 100644 index 000000000..34955205a --- /dev/null +++ b/Java/maven-war-plugin-WarProjectPackagingTask_327/Dockerfile @@ -0,0 +1,18 @@ +FROM ghcr.io/kupl/starlab-benchmarks/java-base:maven-war-plugin + +ENV TZ=Asia/Seoul + +COPY ./metadata.json . +COPY ./npe.json . +COPY ./buggy.java /tmp/buggy.java +RUN export BUGGY_PATH=$(cat metadata.json | jq -r ".npe.filepath") \ + && export BUGGY_LINE=$(cat metadata.json | jq -r ".npe.line") \ + && export BUGGY_MTHD=$(cat metadata.json | jq -r ".npe.npe_method") \ + && mv /tmp/buggy.java $BUGGY_PATH \ + && echo "[{\"filepath\": \"$BUGGY_PATH\", \"line\": $BUGGY_LINE, \"method_name\": \"$BUGGY_MTHD\"}]" | jq . > traces.json + +RUN git init . && git add -A + +RUN $(cat metadata.json | jq -r ".buildCommand") + +RUN $(cat metadata.json | jq -r ".testCommand"); if [ $? -eq 0 ]; then exit 1; fi diff --git a/Java/maven-war-plugin-WarProjectPackagingTask_327/buggy.java b/Java/maven-war-plugin-WarProjectPackagingTask_327/buggy.java new file mode 100644 index 000000000..9edee00b6 --- /dev/null +++ b/Java/maven-war-plugin-WarProjectPackagingTask_327/buggy.java @@ -0,0 +1,380 @@ +package org.apache.maven.plugins.war.packaging; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.File; +import java.io.IOException; +import java.util.Objects; + +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.war.Overlay; +import org.apache.maven.plugins.war.util.PathSet; +import org.apache.maven.shared.filtering.MavenFilteringException; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.StringUtils; + +/** + * Handles the project own resources, that is: + *

    + *
  • The list of web resources, if any
  • + *
  • The content of the webapp directory if it exists
  • + *
  • The custom deployment descriptor(s), if any
  • + *
  • The content of the classes directory if it exists
  • + *
  • The dependencies of the project
  • + *
+ * + * @author Stephane Nicoll + */ +public class WarProjectPackagingTask + extends AbstractWarPackagingTask +{ + private final Resource[] webResources; + + private final File webXml; + + private final File containerConfigXML; + + private final String id; + + private Overlay currentProjectOverlay; + + /** + * @param webResources {@link #webResources} + * @param webXml {@link #webXml} + * @param containerConfigXml {@link #containerConfigXML} + * @param currentProjectOverlay {@link #currentProjectOverlay} + */ + public WarProjectPackagingTask( Resource[] webResources, File webXml, File containerConfigXml, + Overlay currentProjectOverlay ) + { + if ( webResources != null ) + { + this.webResources = webResources; + } + else + { + this.webResources = new Resource[0]; + } + this.webXml = webXml; + this.containerConfigXML = containerConfigXml; + this.currentProjectOverlay = currentProjectOverlay; + this.id = currentProjectOverlay.getId(); + } + + @Override + public void performPackaging( WarPackagingContext context ) + throws MojoExecutionException, MojoFailureException + { + context.getLog().info( "Processing war project" ); + + // Prepare the INF directories + File webinfDir = new File( context.getWebappDirectory(), WEB_INF_PATH ); + webinfDir.mkdirs(); + File metainfDir = new File( context.getWebappDirectory(), META_INF_PATH ); + metainfDir.mkdirs(); + + handleWebResources( context ); + + handleWebAppSourceDirectory( context ); + + // Debug mode: dump the path set for the current build + PathSet pathSet = context.getWebappStructure().getStructure( "currentBuild" ); + context.getLog().debug( "Dump of the current build pathSet content -->" ); + for ( String path : pathSet ) + { + context.getLog().debug( path ); + } + context.getLog().debug( "-- end of dump --" ); + + handleDeploymentDescriptors( context, webinfDir, metainfDir, context.isFailOnMissingWebXml() ); + + handleClassesDirectory( context ); + + handleArtifacts( context ); + + if ( !context.getWebappDirectory().mkdirs() ) + { + context.deleteOutdatedResources(); + } + } + + /** + * Handles the web resources. + * + * @param context the packaging context + * @throws MojoExecutionException if a resource could not be copied + */ + protected void handleWebResources( WarPackagingContext context ) + throws MojoExecutionException + { + for ( Resource resource : webResources ) + { + + // MWAR-246 + if ( resource.getDirectory() == null ) + { + throw new MojoExecutionException( "The tag is missing from the tag." ); + } + + if ( !( new File( resource.getDirectory() ) ).isAbsolute() ) + { + resource.setDirectory( context.getProject().getBasedir() + File.separator + resource.getDirectory() ); + } + + // Make sure that the resource directory is not the same as the webappDirectory + if ( !resource.getDirectory().equals( context.getWebappDirectory().getPath() ) ) + { + + try + { + copyResources( context, resource ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not copy resource [" + resource.getDirectory() + "]", e ); + } + } + } + } + + /** + * Handles the webapp sources. + * + * @param context the packaging context + * @throws MojoExecutionException if the sources could not be copied + */ + protected void handleWebAppSourceDirectory( WarPackagingContext context ) + throws MojoExecutionException + { + // CHECKSTYLE_OFF: LineLength + if ( !context.getWebappSourceDirectory().exists() ) + { + context.getLog().debug( "webapp sources directory does not exist - skipping." ); + } + else if ( !context.getWebappSourceDirectory().getAbsolutePath().equals( context.getWebappDirectory().getPath() ) ) + { + context.getLog().info( "Copying webapp resources [" + context.getWebappSourceDirectory() + "]" ); + final PathSet sources = + getFilesToIncludes( context.getWebappSourceDirectory(), context.getWebappSourceIncludes(), + context.getWebappSourceExcludes(), context.isWebappSourceIncludeEmptyDirectories() ); + + try + { + copyFiles( id, context, context.getWebappSourceDirectory(), sources, false ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not copy webapp sources [" + + context.getWebappDirectory().getAbsolutePath() + "]", e ); + } + } + // CHECKSTYLE_ON: LineLength + } + + /** + * Handles the webapp artifacts. + * + * @param context the packaging context + * @throws MojoExecutionException if the artifacts could not be packaged + */ + protected void handleArtifacts( WarPackagingContext context ) + throws MojoExecutionException + { + ArtifactsPackagingTask task = + new ArtifactsPackagingTask( context.getProject().getArtifacts(), currentProjectOverlay ); + task.performPackaging( context ); + } + + /** + * Handles the webapp classes. + * + * @param context the packaging context + * @throws MojoExecutionException if the classes could not be packaged + */ + protected void handleClassesDirectory( WarPackagingContext context ) + throws MojoExecutionException + { + ClassesPackagingTask task = new ClassesPackagingTask( currentProjectOverlay ); + task.performPackaging( context ); + } + + /** + * Handles the deployment descriptors, if specified. Note that the behavior here is slightly different since the + * customized entry always win, even if an overlay has already packaged a web.xml previously. + * + * @param context the packaging context + * @param webinfDir the web-inf directory + * @param metainfDir the meta-inf directory + * @param failOnMissingWebXml if build should fail if web.xml is not found + * @throws MojoFailureException if the web.xml is specified but does not exist and failOnMissingWebXml is true + * @throws MojoExecutionException if an error occurred while copying the descriptors + */ + protected void handleDeploymentDescriptors( WarPackagingContext context, File webinfDir, File metainfDir, + Boolean failOnMissingWebXml ) + throws MojoFailureException, MojoExecutionException + { + try + { + if ( webXml != null && StringUtils.isNotEmpty( webXml.getName() ) ) + { + if ( !webXml.exists() + && ( failOnMissingWebXml == null || Boolean.TRUE.equals( failOnMissingWebXml ) ) ) + { + throw new MojoFailureException( "The specified web.xml file '" + webXml + "' does not exist" ); + } + + // Making sure that it won't get overlayed + context.getWebappStructure().registerFileForced( id, WEB_INF_PATH + "/web.xml" ); + + if ( context.isFilteringDeploymentDescriptors() ) + { + context.getMavenFileFilter().copyFile( webXml, new File( webinfDir, "web.xml" ), true, + context.getFilterWrappers(), getEncoding( webXml ) ); + } + else + { + copyFile( context, webXml, new File( webinfDir, "web.xml" ), "WEB-INF/web.xml", true ); + } + } + else + { + // the webXml can be the default one + File defaultWebXml = new File( context.getWebappSourceDirectory(), WEB_INF_PATH + "/web.xml" ); + // if exists we can filter it + if ( defaultWebXml.exists() && context.isFilteringDeploymentDescriptors() ) + { + context.getWebappStructure().registerFile( id, WEB_INF_PATH + "/web.xml" ); + context.getMavenFileFilter().copyFile( defaultWebXml, new File( webinfDir, "web.xml" ), true, + context.getFilterWrappers(), getEncoding( defaultWebXml ) ); + } + } + + if ( containerConfigXML != null && StringUtils.isNotEmpty( containerConfigXML.getName() ) ) + { + String xmlFileName = containerConfigXML.getName(); + + context.getWebappStructure().registerFileForced( id, META_INF_PATH + "/" + xmlFileName ); + + if ( context.isFilteringDeploymentDescriptors() ) + { + context.getMavenFileFilter().copyFile( containerConfigXML, new File( metainfDir, xmlFileName ), + true, context.getFilterWrappers(), + getEncoding( containerConfigXML ) ); + } + else + { + copyFile( context, containerConfigXML, new File( metainfDir, xmlFileName ), "META-INF/" + + xmlFileName, true ); + } + } + } + catch ( IOException e ) + { + if ( failOnMissingWebXml == null || Boolean.TRUE.equals( failOnMissingWebXml ) ) + { + throw new MojoExecutionException( "Failed to copy deployment descriptor", e ); + } + } + catch ( MavenFilteringException e ) + { + throw new MojoExecutionException( "Failed to copy deployment descriptor", e ); + } + } + + /** + * Copies webapp webResources from the specified directory. + * + * @param context the WAR packaging context to use + * @param resource the resource to copy + * @throws IOException if an error occurred while copying the resources + * @throws MojoExecutionException if an error occurred while retrieving the filter properties + */ +/** + * Copies webapp webResources from the specified directory. + * + * @param context + * the WAR packaging context to use + * @param resource + * the resource to copy + * @throws IOException + * if an error occurred while copying the resources + * @throws MojoExecutionException + * if an error occurred while retrieving the filter properties + */ +public void copyResources(org.apache.maven.plugins.war.packaging.WarPackagingContext context, org.apache.maven.model.Resource resource) throws java.io.IOException, org.apache.maven.plugin.MojoExecutionException { + if (!context.getWebappDirectory().exists()) { + context.getLog().warn(((("Not copying webapp webResources [" + resource.getDirectory()) + "]: webapp directory [") + context.getWebappDirectory().getAbsolutePath()) + "] does not exist!"); + } + context.getLog().info(((("Copying webapp webResources [" + resource.getDirectory()) + "] to [") + context.getWebappDirectory().getAbsolutePath()) + "]"); + java.lang.String[] fileNames = getFilesToCopy(resource); + for (java.lang.String fileName : fileNames) { + java.lang.String targetFileName = fileName; + { + // TODO make sure this thing is 100% safe + // MWAR-129 if targetPath is only a dot . or ./ + // and the Resource is in a part of the warSourceDirectory the file from sources will override this + // that's we don't have to add the targetPath yep not nice but works + if ((!java.util.Objects.equals(".", /* NPEX_NULL_EXP */ + resource.getTargetPath())) && (!java.util.Objects.equals("./", resource.getTargetPath()))) { + targetFileName = (resource.getTargetPath() + java.io.File.separator) + targetFileName; + } + } + if (resource.isFiltering() && (!context.isNonFilteredExtension(fileName))) { + copyFilteredFile(id, context, new java.io.File(resource.getDirectory(), fileName), targetFileName); + } else { + copyFile(id, context, new java.io.File(resource.getDirectory(), fileName), targetFileName); + } + } +} + + /** + * Returns a list of filenames that should be copied over to the destination directory. + * + * @param resource the resource to be scanned + * @return the array of filenames, relative to the sourceDir + */ + private String[] getFilesToCopy( Resource resource ) + { + // CHECKSTYLE_OFF: LineLength + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( resource.getDirectory() ); + if ( resource.getIncludes() != null && !resource.getIncludes().isEmpty() ) + { + scanner.setIncludes( resource.getIncludes().toArray( new String[resource.getIncludes().size()] ) ); + } + else + { + scanner.setIncludes( DEFAULT_INCLUDES ); + } + if ( resource.getExcludes() != null && !resource.getExcludes().isEmpty() ) + { + scanner.setExcludes( resource.getExcludes().toArray( new String[resource.getExcludes().size()] ) ); + } + + scanner.addDefaultExcludes(); + + scanner.scan(); + + return scanner.getIncludedFiles(); + // CHECKSTYLE_ON: LineLength + } +} diff --git a/Java/maven-war-plugin-WarProjectPackagingTask_327/metadata.json b/Java/maven-war-plugin-WarProjectPackagingTask_327/metadata.json new file mode 100644 index 000000000..075a965fe --- /dev/null +++ b/Java/maven-war-plugin-WarProjectPackagingTask_327/metadata.json @@ -0,0 +1,21 @@ +{ + "language": "java", + "id": "maven-war-plugin-WarProjectPackagingTask_327", + "buggyPath": ".", + "referencePath": null, + "buildCommand": "mvn package -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100 -DskipTests=true -DskipITs=true -Dtest=None -DfailIfNoTests=false", + "testCommand": "mvn test -V -B -Denforcer.skip=true -Dcheckstyle.skip=true -Dcobertura.skip=true -Drat.skip=true -Dlicense.skip=true -Dfindbugs.skip=true -Dgpg.skip=true -Dskip.npm=true -Dskip.gulp=true -Dskip.bower=true -Drat.numUnapprovedLicenses=100", + "categories": [ + "safety", + "npe" + ], + "npe": { + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/WarProjectPackagingTask.java", + "line": 337, + "npe_method": "copyResources", + "deref_field": "getTargetPath", + "npe_class": "WarProjectPackagingTask", + "repo": "maven-war-plugin", + "bug_id": "WarProjectPackagingTask_327" + } +} diff --git a/Java/maven-war-plugin-WarProjectPackagingTask_327/npe.json b/Java/maven-war-plugin-WarProjectPackagingTask_327/npe.json new file mode 100644 index 000000000..031939565 --- /dev/null +++ b/Java/maven-war-plugin-WarProjectPackagingTask_327/npe.json @@ -0,0 +1,7 @@ +{ + "filepath": "src/main/java/org/apache/maven/plugins/war/packaging/WarProjectPackagingTask.java", + "line": 337, + "npe_method": "copyResources", + "deref_field": "getTargetPath", + "npe_class": "WarProjectPackagingTask" +} \ No newline at end of file