adding fatjar and build goals
diff --git a/.gitignore b/.gitignore
index ed7ceec..72ac814 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
*.iws
*.ipr
.asciidoctor
+.site-content
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java b/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java
index 9fa9dbe..0ae9425 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/Ripener.java
@@ -15,10 +15,12 @@
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
+import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import java.io.File;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -194,7 +196,11 @@
final AtomicLong bundleIdGenerator = new AtomicLong(1);
Stream.concat(scanner.findOSGiBundles().stream(), scanner.findPotentialOSGiBundles().stream())
.sorted(this::compareBundles)
- .map(it -> new OSGiBundleLifecycle(it.getManifest(), it.getJar(), services, registry, configuration, bundleIdGenerator.getAndIncrement()))
+ .map(it -> new OSGiBundleLifecycle(
+ it.getManifest(), it.getJar(),
+ services, registry, configuration,
+ bundleIdGenerator.getAndIncrement(),
+ it.getFiles()))
.peek(OSGiBundleLifecycle::start)
.peek(it -> registry.getBundles().put(it.getBundle().getBundleId(), it))
.forEach(bundle -> LOGGER.debug("Bundle {}", bundle));
@@ -276,7 +282,61 @@
static void main(final String[] args) {
final CountDownLatch latch = new CountDownLatch(1);
- final Ripener main = new Impl(new Configuration()).start();
+ final Configuration configuration = new Configuration();
+ ofNullable(System.getProperty("winegrower.ripener.configuration.workdir"))
+ .map(String::valueOf)
+ .map(File::new)
+ .ifPresent(configuration::setWorkDir);
+ ofNullable(System.getProperty("winegrower.ripener.configuration.prioritizedBundles"))
+ .map(String::valueOf)
+ .filter(it -> !it.isEmpty())
+ .map(it -> asList(it.split(",")))
+ .ifPresent(configuration::setPrioritizedBundles);
+ ofNullable(System.getProperty("winegrower.ripener.configuration.ignoredBundles"))
+ .map(String::valueOf)
+ .filter(it -> !it.isEmpty())
+ .map(it -> asList(it.split(",")))
+ .ifPresent(configuration::setIgnoredBundles);
+ ofNullable(System.getProperty("winegrower.ripener.configuration.scanningIncludes"))
+ .map(String::valueOf)
+ .filter(it -> !it.isEmpty())
+ .map(it -> asList(it.split(",")))
+ .ifPresent(configuration::setScanningIncludes);
+ ofNullable(System.getProperty("winegrower.ripener.configuration.scanningExcludes"))
+ .map(String::valueOf)
+ .filter(it -> !it.isEmpty())
+ .map(it -> asList(it.split(",")))
+ .ifPresent(configuration::setScanningExcludes);
+ ofNullable(System.getProperty("winegrower.ripener.configuration.manifestContributors"))
+ .map(String::valueOf)
+ .filter(it -> !it.isEmpty())
+ .map(it -> asList(it.split(",")))
+ .ifPresent(contributors -> {
+ configuration.setManifestContributors(contributors.stream().map(clazz -> {
+ try {
+ return Thread.currentThread().getContextClassLoader().loadClass(clazz).getConstructor().newInstance();
+ } catch (final InstantiationException | NoSuchMethodException | IllegalAccessException
+ | ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ } catch (final InvocationTargetException e) {
+ throw new IllegalArgumentException(e.getTargetException());
+ }
+ }).map(ManifestContributor.class::cast).collect(toList()));
+ });
+ ofNullable(System.getProperty("winegrower.ripener.configuration.jarFilter"))
+ .map(String::valueOf)
+ .filter(it -> !it.isEmpty())
+ .ifPresent(filter -> {
+ try {
+ configuration.setJarFilter((Predicate<String>) Thread.currentThread().getContextClassLoader().loadClass(filter)
+ .getConstructor().newInstance());
+ } catch (final InstantiationException | NoSuchMethodException | IllegalAccessException | ClassNotFoundException e) {
+ throw new IllegalArgumentException(e);
+ } catch (final InvocationTargetException e) {
+ throw new IllegalArgumentException(e.getTargetException());
+ }
+ });
+ final Ripener main = new Impl(configuration).start();
Runtime.getRuntime().addShutdownHook(new Thread() {
{
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java
index d523b69..721109e 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/BundleImpl.java
@@ -64,15 +64,19 @@
private final String symbolicName;
private final Dictionary<String, String> headers;
private final File dataFileBase;
+ private final Collection<String> includedResources;
private int state = Bundle.UNINSTALLED;
BundleImpl(final Manifest manifest, final File file, final BundleContextImpl context,
- final Ripener.Configuration configuration, final long id) {
+ final Ripener.Configuration configuration, final long id,
+ final Collection<String> includedResources) {
this.file = file;
- this.dataFileBase = new File(configuration.getWorkDir(), file.getName());
+ this.dataFileBase = new File(configuration.getWorkDir(),
+ file == null ? Long.toString(System.identityHashCode(manifest)) : file.getName());
this.context = context;
this.id = id;
this.loader = Thread.currentThread().getContextClassLoader();
+ this.includedResources = includedResources;
this.version = ofNullable(manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION))
.map(Version::new)
.orElse(Version.emptyVersion);
@@ -161,7 +165,7 @@
@Override
public String getLocation() {
- return file.getAbsolutePath();
+ return includedResources != null ? null : file.getAbsolutePath();
}
@Override
@@ -209,6 +213,11 @@
@Override
public Enumeration<String> getEntryPaths(final String path) {
+ if (includedResources != null) {
+ return enumeration(includedResources.stream()
+ .filter(it -> it.startsWith(path))
+ .collect(toList()));
+ }
if (file.isDirectory()) {
final Path base = file.toPath().toAbsolutePath();
final Path subPath = new File(file, path == null ? "" : (path.startsWith("/") ? path.substring(1) : path)).toPath();
@@ -245,7 +254,7 @@
@Override
public long getLastModified() {
- return file.lastModified();
+ return file == null ? -1 : file.lastModified();
}
@Override
@@ -253,6 +262,16 @@
final Filter filter = filePattern == null ?
null : context.createFilter("(filename=" + filePattern + ")");
final String prefix = path == null ? "" : (path.startsWith("/") ? path.substring(1) : path);
+
+ if (includedResources != null) {
+ if (!recurse) {
+ return enumeration(includedResources.stream()
+ .filter(it -> doFilterEntry(filter, prefix, it))
+ .map(loader::getResource)
+ .collect(toList()));
+ }
+ }
+
final File baseFile = new File(file, prefix);
final Path base = baseFile.toPath();
final Path filePath = this.file.toPath();
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java b/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java
index ca1c3b3..82efc3c 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/deployer/OSGiBundleLifecycle.java
@@ -15,6 +15,7 @@
import java.io.File;
import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
import java.util.jar.Manifest;
import org.apache.winegrower.Ripener;
@@ -34,9 +35,9 @@
public OSGiBundleLifecycle(final Manifest manifest, final File file, final OSGiServices services,
final BundleRegistry registry, final Ripener.Configuration configuration,
- final long id) {
+ final long id, final Collection<String> includedResources) {
this.context = new BundleContextImpl(manifest, services, this::getBundle, registry);
- this.bundle = new BundleImpl(manifest, file, context, configuration, id);
+ this.bundle = new BundleImpl(manifest, file, context, configuration, id, includedResources);
}
public BundleActivatorHandler getActivator() {
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/scanner/StandaloneScanner.java b/winegrower-core/src/main/java/org/apache/winegrower/scanner/StandaloneScanner.java
index 7817fc6..03d721a 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/scanner/StandaloneScanner.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/scanner/StandaloneScanner.java
@@ -13,20 +13,29 @@
*/
package org.apache.winegrower.scanner;
+import static java.util.Arrays.asList;
+import static java.util.Collections.list;
+import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
import static org.apache.xbean.finder.archive.ClasspathArchive.archive;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
+import java.util.stream.Stream;
import org.apache.winegrower.Ripener;
import org.apache.winegrower.scanner.manifest.ManifestCreator;
@@ -46,6 +55,8 @@
private final Ripener.Configuration configuration;
private final ClassLoader loader;
private final File frameworkJar;
+ private final Map<String, Manifest> providedManifests;
+ private final Map<String, List<String>> providedIndex;
public StandaloneScanner(final Ripener.Configuration configuration, final File frameworkJar) {
this.configuration = configuration;
@@ -58,6 +69,47 @@
} catch (final IOException e) {
throw new IllegalStateException(e);
}
+
+ try { // fatjar plugin
+ providedManifests = list(this.loader.getResources("WINEGROWER-INF/manifests.properties")).stream()
+ .flatMap(url -> {
+ final Properties properties = new Properties();
+ try (final InputStream stream = url.openStream()) {
+ properties.load(stream);
+ } catch (final IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return properties.stringPropertyNames().stream()
+ .collect(toMap(identity(), key -> {
+ final String property = properties.getProperty(key);
+ final Manifest manifest = new Manifest();
+ try (final ByteArrayInputStream mfStream = new ByteArrayInputStream(property.getBytes(StandardCharsets.UTF_8))) {
+ manifest.read(mfStream);
+ } catch (final IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return manifest;
+ })).entrySet().stream();
+ })
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ providedIndex = list(this.loader.getResources("WINEGROWER-INF/index.properties")).stream()
+ .flatMap(url -> {
+ final Properties properties = new Properties();
+ try (final InputStream stream = url.openStream()) {
+ properties.load(stream);
+ } catch (final IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ return properties.stringPropertyNames().stream()
+ .collect(toMap(identity(), key -> {
+ final String property = properties.getProperty(key);
+ return asList(property.split(","));
+ })).entrySet().stream();
+ })
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ } catch (final IOException e) {
+ throw new IllegalArgumentException(e);
+ }
}
public Collection<BundleDefinition> findPotentialOSGiBundles() {
@@ -81,7 +133,7 @@
return null;
}
LOGGER.debug("{} was scanned and is converted to a bundle", it.file);
- return new BundleDefinition(manifest, it.file);
+ return new BundleDefinition(manifest, it.file, null);
} catch (final LinkageError e) {
LOGGER.debug("{} is not scannable, maybe exclude it in framework configuration", it.file);
return null;
@@ -92,13 +144,15 @@
}
public Collection<BundleDefinition> findOSGiBundles() {
- return urls
- .stream()
- .map(Files::toFile)
- .filter(this::isIncluded)
- .filter(it -> this.configuration.getIgnoredBundles().stream().noneMatch(ex -> it.getName().startsWith(ex)))
- .map(this::toDefinition)
- .filter(Objects::nonNull)
+ return Stream.concat(
+ urls.stream()
+ .map(Files::toFile)
+ .filter(this::isIncluded)
+ .filter(it -> this.configuration.getIgnoredBundles().stream().noneMatch(ex -> it.getName().startsWith(ex)))
+ .map(this::toDefinition)
+ .filter(Objects::nonNull),
+ providedManifests.entrySet().stream()
+ .map(it -> new BundleDefinition(it.getValue(), null, providedIndex.get(it.getKey()))))
.collect(toList());
}
@@ -113,7 +167,7 @@
try (final InputStream stream = new FileInputStream(manifest)) {
final Manifest mf = new Manifest(stream);
if (isOSGi(mf)) {
- return new BundleDefinition(mf, file);
+ return new BundleDefinition(mf, file, null);
}
return null;
} catch (final IOException e) {
@@ -128,7 +182,7 @@
return null;
}
if (isOSGi(manifest)) {
- return new BundleDefinition(manifest, file);
+ return new BundleDefinition(manifest, file, null);
}
return null;
} catch (final Exception e) {
@@ -143,10 +197,16 @@
public static class BundleDefinition {
private final Manifest manifest;
private final File jar;
+ private final Collection<String> files;
- private BundleDefinition(final Manifest manifest, final File jar) {
+ private BundleDefinition(final Manifest manifest, final File jar, final Collection<String> files) {
this.manifest = manifest;
this.jar = jar;
+ this.files = files;
+ }
+
+ public Collection<String> getFiles() {
+ return files;
}
public Manifest getManifest() {
diff --git a/winegrower-core/src/main/java/org/apache/winegrower/service/BundleRegistry.java b/winegrower-core/src/main/java/org/apache/winegrower/service/BundleRegistry.java
index cdeb85e..1352de7 100644
--- a/winegrower-core/src/main/java/org/apache/winegrower/service/BundleRegistry.java
+++ b/winegrower-core/src/main/java/org/apache/winegrower/service/BundleRegistry.java
@@ -40,7 +40,7 @@
frameworkManifest.getMainAttributes().putValue("Bundle-Version", "1.0");
frameworkManifest.getMainAttributes().putValue("Bundle-SymbolicName", "Ripener");
final OSGiBundleLifecycle frameworkBundle = new OSGiBundleLifecycle(
- frameworkManifest, framework, services, this, configuration, 0L);
+ frameworkManifest, framework, services, this, configuration, 0L, null);
frameworkBundle.start();
bundles.put(0L, frameworkBundle);
}
diff --git a/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java b/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java
index db93eb1..232fbb9 100644
--- a/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java
+++ b/winegrower-core/src/test/java/org/apache/winegrower/deployer/BundleImplTest.java
@@ -49,8 +49,8 @@
registry = new BundleRegistry(services, configuration);
final BundleContextImpl context = new BundleContextImpl(manifest, services, () -> bundle, registry);
final File file = new File(registry.getFramework().getParentFile(), "test-classes");
- bundle = new BundleImpl(manifest, file, context, configuration, 1);
- registry.getBundles().put(bundle.getBundleId(), new OSGiBundleLifecycle(manifest, file, services, registry, configuration, 1));
+ bundle = new BundleImpl(manifest, file, context, configuration, 1, null);
+ registry.getBundles().put(bundle.getBundleId(), new OSGiBundleLifecycle(manifest, file, services, registry, configuration, 1, null));
}
@Test
diff --git a/winegrower-documentation/pom.xml b/winegrower-documentation/pom.xml
index 9330201..c94ffe1 100644
--- a/winegrower-documentation/pom.xml
+++ b/winegrower-documentation/pom.xml
@@ -35,20 +35,6 @@
<groovy.version>2.5.3</groovy.version>
</properties>
- <dependencies>
-
- <dependency>
- <groupId>org.codehaus.groovy</groupId>
- <artifactId>groovy-all</artifactId>
- <version>${groovy.version}</version>
- <type>pom</type>
- </dependency>
- <dependency>
- <groupId>org.asciidoctor</groupId>
- <artifactId>asciidoctorj</artifactId>
- <version>1.5.7</version>
- </dependency>
- </dependencies>
<build>
<plugins>
<plugin>
@@ -118,37 +104,26 @@
<build>
<plugins>
<plugin>
- <groupId>org.codehaus.gmavenplus</groupId>
- <artifactId>gmavenplus-plugin</artifactId>
- <version>1.6</version>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-scm-publish-plugin</artifactId>
+ <version>1.1</version>
<executions>
<execution>
- <id>deploy-site</id>
- <phase>none</phase>
+ <id>scm-publish</id>
+ <phase>site-deploy</phase>
<goals>
- <goal>execute</goal>
+ <goal>publish-scm</goal>
</goals>
- <configuration>
- <allowSystemExits>true</allowSystemExits>
- <scripts>
- <script>${project.basedir}/src/build/GithubPages.groovy</script>
- </scripts>
- </configuration>
</execution>
</executions>
- <dependencies>
- <dependency>
- <groupId>org.eclipse.jgit</groupId>
- <artifactId>org.eclipse.jgit</artifactId>
- <version>4.9.2.201712150930-r</version>
- </dependency>
- <dependency>
- <groupId>org.codehaus.groovy</groupId>
- <artifactId>groovy-all</artifactId>
- <version>${groovy.version}</version>
- <type>pom</type>
- </dependency>
- </dependencies>
+ <configuration>
+ <content>${project.build.directory}/documentation</content>
+ <pubScmUrl>scm:git:${project.scm.connection}</pubScmUrl>
+ <scmBranch>gh-pages</scmBranch>
+ <tryUpdate>true</tryUpdate>
+ <checkoutDirectory>${project.parent.basedir}/.site-content</checkoutDirectory>
+ <serverId>${github.serverId}</serverId>
+ </configuration>
</plugin>
</plugins>
</build>
diff --git a/winegrower-documentation/src/build/GithubPages.groovy b/winegrower-documentation/src/build/GithubPages.groovy
deleted file mode 100644
index 8892443..0000000
--- a/winegrower-documentation/src/build/GithubPages.groovy
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Licensed 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
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * 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.winegrower.build
-
-import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest
-import org.apache.maven.settings.crypto.SettingsDecrypter
-import org.eclipse.jgit.api.Git
-import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider
-
-import static java.util.Collections.singleton
-
-def source = new File(project.build.directory, 'documentation')
-if (!source.exists() || !new File(source, 'index.html').exists()) {
- log.warn('Not ready to deploy, skipping')
- return
-}
-
-def branch = 'refs/heads/gh-pages'
-def workDir = new File(project.build.directory, UUID.randomUUID().toString() + '_' + System.currentTimeMillis())
-
-def url = project.parent.scm.url
-def serverId = project.properties['github.serverId']
-log.info("Using server ${serverId}")
-
-def server = session.settings.servers.findAll { it.id == serverId }.iterator().next()
-def decryptedServer = session.container.lookup(SettingsDecrypter).decrypt(new DefaultSettingsDecryptionRequest(server))
-server = decryptedServer.server != null ? decryptedServer.server : server
-
-log.info("Using url=${url}")
-log.info("Using user=${server.username}")
-log.info("Using branch=${branch}")
-
-def credentialsProvider = new UsernamePasswordCredentialsProvider(server.username, server.password)
-def git = Git.cloneRepository()
- .setCredentialsProvider(credentialsProvider)
- .setURI(url)
- .setDirectory(workDir)
- .setBranchesToClone(singleton(branch))
- .setBranch(branch)
- .call()
-
-new AntBuilder().copy(todir: workDir.absolutePath, overwrite: true) {
- fileset(dir: source.absolutePath)
-}
-// we don't drop old files, stay conservative for now
-
-def message = "Updating the documentation for version ${project.version} // " + new Date().toString()
-git.add().addFilepattern(".").call()
-git.commit().setAll(true).setMessage(message).call()
-git.status().call()
-git.push().setCredentialsProvider(credentialsProvider).add(branch).call()
-log.info("Updated the documentation on ${new Date()}")
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/pom.xml b/winegrower-extension/winegrower-build/winegrower-build-common/pom.xml
index ad166dd..362fbc7 100644
--- a/winegrower-extension/winegrower-build/winegrower-build-common/pom.xml
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/pom.xml
@@ -42,6 +42,16 @@
<scope>compile</scope>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-compress</artifactId>
+ <version>1.18</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-text</artifactId>
+ <version>1.6</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.winegrower</groupId>
<artifactId>winegrower-testing-junit5</artifactId>
<version>${project.version}</version>
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/Build.java b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/Build.java
new file mode 100644
index 0000000..89a40d8
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/Build.java
@@ -0,0 +1,269 @@
+/**
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.extension.build.common;
+
+import static java.util.Arrays.asList;
+import static java.util.Locale.ENGLISH;
+import static java.util.stream.Collectors.joining;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.stream.Stream;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
+import org.apache.commons.text.StringSubstitutor;
+
+public class Build implements Runnable {
+ private final Configuration configuration;
+
+ public Build(final Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public void run() {
+ final File distroFolder = new File(configuration.workDir, configuration.artifactId + "-winegrower-distribution");
+ if (distroFolder.exists()) {
+ delete(distroFolder);
+ }
+
+ Stream.of("bin", "conf", "logs", "lib").forEach(i -> new File(distroFolder, i).mkdirs());
+
+ for (final String ext : asList("sh", "bat")) {
+ try (final BufferedReader reader = new BufferedReader(new InputStreamReader(
+ Thread.currentThread().getContextClassLoader().getResourceAsStream("bin/winegrower." + ext)))) {
+ write(new File(distroFolder, "bin/winegrower." + ext), StringSubstitutor.replace(reader.lines().collect(joining("\n")),
+ new HashMap<String, String>() {{
+ put("main", configuration.main);
+ }}));
+ } catch (final IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ copyProvidedFiles(configuration.basedir, distroFolder);
+
+ Stream.of("conf", "logs").forEach(folder ->
+ write(new File(distroFolder, folder + "/you_can_safely_delete.txt"), "Just there to not loose the folder cause it is empty, you can safely delete."));
+
+ configuration.jars.forEach(it -> addLib(distroFolder, it));
+
+ final Path prefix = configuration.skipArchiveRootFolder ? distroFolder.toPath() : distroFolder.getParentFile().toPath();
+ for (final String format : configuration.formats) {
+ final File output = new File(configuration.workDir, configuration.artifactId + "-winegrower-distribution." + format);
+
+ switch (format.toLowerCase(ENGLISH)) {
+ case "tar.gz":
+ try (final TarArchiveOutputStream tarGz =
+ new TarArchiveOutputStream(new GZIPOutputStream(new FileOutputStream(output)))) {
+ tarGz.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
+ for (final String entry : distroFolder.list()) {
+ tarGz(tarGz, new File(distroFolder, entry), prefix);
+ }
+ } catch (final IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ break;
+ case "zip":
+ try (final ZipArchiveOutputStream zos =
+ new ZipArchiveOutputStream(new FileOutputStream(output))) {
+ for (final String entry : distroFolder.list()) {
+ zip(zos, new File(distroFolder, entry), prefix);
+ }
+ } catch (final IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException(format + " is not supported");
+ }
+ }
+
+ if (!configuration.keepExplodedFolder) {
+ delete(distroFolder);
+ }
+ }
+
+ private void copyProvidedFiles(final File basedir, final File base) {
+ final File srcConf = new File(basedir, configuration.conf);
+ if (srcConf.exists() && srcConf.isDirectory()) {
+ final File targetConf = new File(base, "conf");
+ targetConf.mkdirs();
+
+ for (final File file : srcConf.listFiles()) {
+ final String fileName = file.getName();
+ if (fileName.startsWith(".")) {
+ // hidden file -> ignore
+ continue;
+ }
+
+ try {
+ Files.copy(file.toPath(), new File(targetConf, fileName).toPath());
+ } catch (final IOException e) {
+ throw new IllegalStateException("Could not copy file " + file.getAbsolutePath(), e);
+ }
+ }
+ }
+
+ final File srcBin = new File(basedir, configuration.bin);
+ if (srcBin.exists() && srcBin.isDirectory()) {
+ final File targetRoot = new File(base, "bin");
+ targetRoot.mkdirs();
+ Stream.of(srcBin.listFiles())
+ .filter(f -> !f.isDirectory()) // not nested for now
+ .forEach(f -> {
+ try {
+ final File target = new File(targetRoot, f.getName());
+ Files.copy(f.toPath(), target.toPath());
+ if (target.getName().endsWith(".sh")) {
+ target.setExecutable(true);
+ }
+ } catch (final IOException e) {
+ throw new IllegalArgumentException("Could not copy file " + f.getAbsolutePath(), e);
+ }
+ });
+ }
+ }
+
+ private void write(final File file, final String content) {
+ try {
+ Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private void delete(final File file) { // not critical
+ final Path rootPath = file.toPath();
+ try {
+ Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+
+ });
+ } catch (final IOException e) {
+ // no-op
+ }
+ }
+
+ private void addLib(final File base, final File cc) {
+ try {
+ Files.copy(cc.toPath(), new File(base, "lib/" + cc.getName()).toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } catch (final IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ private void zip(final ZipArchiveOutputStream zip, final File f, final Path prefix) throws IOException {
+ final String path = prefix.relativize(f.toPath()).toString().replace(File.separator, "/");
+ final ZipArchiveEntry archiveEntry = new ZipArchiveEntry(f, path);
+ if (isSh(path)) {
+ archiveEntry.setUnixMode(0755);
+ }
+ zip.putArchiveEntry(archiveEntry);
+ if (f.isDirectory()) {
+ zip.closeArchiveEntry();
+ final File[] files = f.listFiles();
+ if (files != null) {
+ for (final File child : files) {
+ zip(zip, child, prefix);
+ }
+ }
+ } else {
+ Files.copy(f.toPath(), zip);
+ zip.closeArchiveEntry();
+ }
+ }
+
+ private void tarGz(final TarArchiveOutputStream tarGz, final File f, final Path prefix) throws IOException {
+ final String path = prefix.relativize(f.toPath()).toString().replace(File.separator, "/");
+ final TarArchiveEntry archiveEntry = new TarArchiveEntry(f, path);
+ if (isSh(path)) {
+ archiveEntry.setMode(0755);
+ }
+ tarGz.putArchiveEntry(archiveEntry);
+ if (f.isDirectory()) {
+ tarGz.closeArchiveEntry();
+ final File[] files = f.listFiles();
+ if (files != null) {
+ for (final File child : files) {
+ tarGz(tarGz, child, prefix);
+ }
+ }
+ } else {
+ Files.copy(f.toPath(), tarGz);
+ tarGz.closeArchiveEntry();
+ }
+ }
+
+ private boolean isSh(final String path) {
+ return path.endsWith(".sh");
+ }
+
+ public static class Configuration {
+ private final File workDir;
+ private final File basedir;
+ private final String artifactId;
+ private final Collection<File> jars;
+ private final Collection<String> formats;
+ private final String main;
+ private final String bin;
+ private final String conf;
+ private final boolean skipArchiveRootFolder;
+ private final boolean keepExplodedFolder;
+
+ public Configuration(final File workDir, final File basedir,
+ final String artifactId, final Collection<File> jars,
+ final Collection<String> formats,
+ final String main, final String bin, final String conf,
+ final boolean skipArchiveRootFolder, final boolean keepExplodedFolder) {
+ this.workDir = workDir;
+ this.basedir = basedir;
+ this.artifactId = artifactId;
+ this.jars = jars;
+ this.formats = formats;
+ this.main = main;
+ this.bin = bin;
+ this.conf = conf;
+ this.skipArchiveRootFolder = skipArchiveRootFolder;
+ this.keepExplodedFolder = keepExplodedFolder;
+ }
+ }
+}
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/FatJar.java b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/FatJar.java
new file mode 100644
index 0000000..25119c0
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/java/org/apache/winegrower/extension/build/common/FatJar.java
@@ -0,0 +1,114 @@
+/**
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.extension.build.common;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+public class FatJar implements Runnable {
+ private final Configuration configuration;
+
+ public FatJar(final Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public void run() {
+ requireNonNull(configuration.jars, "Jars are not set");
+ requireNonNull(configuration.output, "Output is not set").getParentFile().mkdirs();
+
+ try (final JarOutputStream outputStream = new JarOutputStream(
+ new BufferedOutputStream(new FileOutputStream(configuration.output)))) {
+ final Properties manifests = new Properties();
+ final Properties index = new Properties();
+ byte[] buffer = new byte[8192];
+ final Set<String> alreadyAdded = new HashSet<>();
+ configuration.jars.forEach(shadedJar -> {
+ final Collection<String> files = new ArrayList<>();
+ try (final JarInputStream inputStream = new JarInputStream(new BufferedInputStream(new FileInputStream(shadedJar)))) {
+ final Manifest manifest = inputStream.getManifest();
+ if (manifest != null) {
+ try (final ByteArrayOutputStream manifestStream = new ByteArrayOutputStream()) {
+ manifest.write(manifestStream);
+ manifestStream.flush();
+ manifests.put(shadedJar.getName(), new String(manifestStream.toByteArray(), StandardCharsets.UTF_8));
+ }
+ }
+
+ ZipEntry nextEntry;
+ while ((nextEntry = inputStream.getNextEntry()) != null) {
+ final String name = nextEntry.getName();
+ if (!alreadyAdded.add(name)) {
+ continue;
+ }
+ files.add(name);
+ outputStream.putNextEntry(nextEntry);
+ int count;
+ while ((count = inputStream.read(buffer, 0, buffer.length)) >= 0) {
+ if (count > 0) {
+ outputStream.write(buffer, 0, count);
+ }
+ }
+ outputStream.closeEntry();
+ }
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ index.put(shadedJar.getName(), files.stream().collect(joining(",")));
+ });
+
+ outputStream.putNextEntry(new JarEntry("WINEGROWER-INF/"));
+ outputStream.closeEntry();
+
+ outputStream.putNextEntry(new JarEntry("WINEGROWER-INF/index.properties"));
+ index.store(outputStream, "index");
+ outputStream.closeEntry();
+
+ outputStream.putNextEntry(new JarEntry("WINEGROWER-INF/manifests.properties"));
+ manifests.store(outputStream, "manifests");
+ outputStream.closeEntry();
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public static class Configuration {
+ private final Collection<File> jars;
+ private final File output;
+
+ public Configuration(final Collection<File> jars, final File output) {
+ this.jars = jars;
+ this.output = output;
+ }
+ }
+}
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/main/resources/bin/winegrower.bat b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/resources/bin/winegrower.bat
new file mode 100644
index 0000000..0bbee0b
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/resources/bin/winegrower.bat
@@ -0,0 +1,374 @@
+@echo off
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+
+rem ---------------------------------------------------------------------------
+rem Start/Stop Script for the WINEGROWER Server
+rem
+rem Environment Variable Prerequisites
+rem
+rem Do not set the variables in this script. Instead put them into a script
+rem setenv.bat in WINEGROWER_BASE/bin to keep your customizations separate.
+rem
+rem WHEN RUNNING WINEGROWER AS A WINDOWS SERVICE:
+rem Note that the environment variables that affect the behavior of this
+rem script will have no effect at all on Windows Services. As such, any
+rem local customizations made in a WINEGROWER_BASE/bin/setenv.bat script
+rem will also have no effect on Winegrower when launched as a Windows Service.
+rem The configuration that controls Windows Services is stored in the Windows
+rem Registry, and is most conveniently maintained using the "tomcatXw.exe"
+rem maintenance utility, where "X" is the major version of Tomcat embedded
+rem in Winegrower you are running.
+rem
+rem WINEGROWER_HOME May point at your WINEGROWER "build" directory.
+rem
+rem WINEGROWER_BASE (Optional) Base directory for resolving dynamic portions
+rem of a WINEGROWER installation. If not present, resolves to
+rem the same directory that WINEGROWER_HOME points to.
+rem
+rem WINEGROWER_OPTS (Optional) Java runtime options used when the "start",
+rem "run" or "debug" command is executed.
+rem Include here and not in JAVA_OPTS all options, that should
+rem only be used by Winegrower itself, not by the stop process,
+rem the version command etc.
+rem Examples are heap size, GC logging, JMX ports etc.
+rem
+rem WINEGROWER_TMPDIR (Optional) Directory path location of temporary directory
+rem the JVM should use (java.io.tmpdir). Defaults to
+rem %WINEGROWER_BASE%\temp.
+rem
+rem JAVA_HOME Must point at your Java Development Kit installation.
+rem Required to run the with the "debug" argument.
+rem
+rem JRE_HOME Must point at your Java Runtime installation.
+rem Defaults to JAVA_HOME if empty. If JRE_HOME and JAVA_HOME
+rem are both set, JRE_HOME is used.
+rem
+rem JAVA_OPTS (Optional) Java runtime options used when any command
+rem is executed.
+rem Include here and not in WINEGROWER_OPTS all options, that
+rem should be used by Winegrower and also by the stop process,
+rem the version command etc.
+rem Most options should go into WINEGROWER_OPTS.
+rem
+rem JAVA_ENDORSED_DIRS (Optional) Lists of of semi-colon separated directories
+rem containing some jars in order to allow replacement of APIs
+rem created outside of the JCP (i.e. DOM and SAX from W3C).
+rem It can also be used to update the XML parser implementation.
+rem This is only supported for Java <= 8.
+rem Defaults to $WINEGROWER_HOME/endorsed.
+rem
+rem JPDA_TRANSPORT (Optional) JPDA transport used when the "jpda start"
+rem command is executed. The default is "dt_socket".
+rem
+rem JPDA_ADDRESS (Optional) Java runtime options used when the "jpda start"
+rem command is executed. The default is localhost:8000.
+rem
+rem JPDA_SUSPEND (Optional) Java runtime options used when the "jpda start"
+rem command is executed. Specifies whether JVM should suspend
+rem execution immediately after startup. Default is "n".
+rem
+rem JPDA_OPTS (Optional) Java runtime options used when the "jpda start"
+rem command is executed. If used, JPDA_TRANSPORT, JPDA_ADDRESS,
+rem and JPDA_SUSPEND are ignored. Thus, all required jpda
+rem options MUST be specified. The default is:
+rem
+rem -agentlib:jdwp=transport=%JPDA_TRANSPORT%,
+rem address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
+rem
+rem JSSE_OPTS (Optional) Java runtime options used to control the TLS
+rem implementation when JSSE is used. Default is:
+rem "-Djdk.tls.ephemeralDHKeySize=2048"
+rem
+rem LOGGING_CONFIG (Optional) Override Winegrower's logging config file
+rem Example (all one line)
+rem set LOGGING_CONFIG="-Djava.util.logging.config.file=%WINEGROWER_BASE%\conf\logging.properties"
+rem
+rem LOGGING_MANAGER (Optional) Override Winegrower's logging manager
+rem Example (all one line)
+rem set LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
+rem
+rem TITLE (Optional) Specify the title of Winegrower window. The default
+rem TITLE is Winegrower if it's not specified.
+rem Example (all one line)
+rem set TITLE=Winegrower.Cluster#1.Server#1 [%DATE% %TIME%]
+rem ---------------------------------------------------------------------------
+
+setlocal
+
+rem Suppress Terminate batch job on CTRL+C
+if not ""%1"" == ""run"" goto mainEntry
+if "%TEMP%" == "" goto mainEntry
+if exist "%TEMP%\%~nx0.run" goto mainEntry
+echo Y>"%TEMP%\%~nx0.run"
+if not exist "%TEMP%\%~nx0.run" goto mainEntry
+echo Y>"%TEMP%\%~nx0.Y"
+call "%~f0" %* <"%TEMP%\%~nx0.Y"
+rem Use provided errorlevel
+set RETVAL=%ERRORLEVEL%
+del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1
+exit /B %RETVAL%
+:mainEntry
+del /Q "%TEMP%\%~nx0.run" >NUL 2>&1
+
+rem Guess WINEGROWER_HOME if not defined
+set "CURRENT_DIR=%cd%"
+if not "%WINEGROWER_HOME%" == "" goto gotHome
+set "WINEGROWER_HOME=%CURRENT_DIR%"
+if exist "%WINEGROWER_HOME%\bin\WINEGROWER.bat" goto okHome
+cd ..
+set "WINEGROWER_HOME=%cd%"
+cd "%CURRENT_DIR%"
+:gotHome
+
+if exist "%WINEGROWER_HOME%\bin\WINEGROWER.bat" goto okHome
+echo The WINEGROWER_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+rem Copy WINEGROWER_BASE from WINEGROWER_HOME if not defined
+if not "%WINEGROWER_BASE%" == "" goto gotBase
+set "WINEGROWER_BASE=%WINEGROWER_HOME%"
+:gotBase
+
+rem Ensure that neither WINEGROWER_HOME nor WINEGROWER_BASE contains a semi-colon
+rem as this is used as the separator in the classpath and Java provides no
+rem mechanism for escaping if the same character appears in the path. Check this
+rem by replacing all occurrences of ';' with '' and checking that neither
+rem WINEGROWER_HOME nor WINEGROWER_BASE have changed
+if "%WINEGROWER_HOME%" == "%WINEGROWER_HOME:;=%" goto homeNoSemicolon
+echo Using WINEGROWER_HOME: "%WINEGROWER_HOME%"
+echo Unable to start as WINEGROWER_HOME contains a semicolon (;) character
+goto end
+:homeNoSemicolon
+
+if "%WINEGROWER_BASE%" == "%WINEGROWER_BASE:;=%" goto baseNoSemicolon
+echo Using WINEGROWER_BASE: "%WINEGROWER_BASE%"
+echo Unable to start as WINEGROWER_BASE contains a semicolon (;) character
+goto end
+:baseNoSemicolon
+
+rem Ensure that any user defined CLASSPATH variables are not used on startup,
+rem but allow them to be specified in setenv.bat, in rare case when it is needed.
+set CLASSPATH=
+
+rem Get standard environment variables
+if not exist "%WINEGROWER_BASE%\bin\setenv.bat" goto checkSetenvHome
+call "%WINEGROWER_BASE%\bin\setenv.bat"
+goto setenvDone
+:checkSetenvHome
+if exist "%WINEGROWER_HOME%\bin\setenv.bat" call "%WINEGROWER_HOME%\bin\setenv.bat"
+:setenvDone
+
+rem Get standard Java environment variables
+rem In debug mode we need a real JDK (JAVA_HOME)
+if ""%1"" == ""debug"" goto needJavaHome
+
+rem Otherwise either JRE or JDK are fine
+if not "%JRE_HOME%" == "" goto gotJreHome
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo Neither the JAVA_HOME nor the JRE_HOME environment variable is defined
+echo At least one of these environment variable is needed to run this program
+goto exit
+
+:needJavaHome
+rem Check if we have a usable JDK
+if "%JAVA_HOME%" == "" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\javaw.exe" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\jdb.exe" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\javac.exe" goto noJavaHome
+set "JRE_HOME=%JAVA_HOME%"
+goto okJava
+
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly.
+echo It is needed to run this program in debug mode.
+echo NB: JAVA_HOME should point to a JDK not a JRE.
+goto exit
+
+:gotJavaHome
+rem No JRE given, use JAVA_HOME as JRE_HOME
+set "JRE_HOME=%JAVA_HOME%"
+
+:gotJreHome
+rem Check if we have a usable JRE
+if not exist "%JRE_HOME%\bin\java.exe" goto noJreHome
+if not exist "%JRE_HOME%\bin\javaw.exe" goto noJreHome
+goto okJava
+
+:noJreHome
+rem Needed at least a JRE
+echo The JRE_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto exit
+
+:okJava
+rem Don't override the endorsed dir if the user has set it previously
+if not "%JAVA_ENDORSED_DIRS%" == "" goto gotEndorseddir
+rem Java 9 no longer supports the java.endorsed.dirs
+rem system property. Only try to use it if
+rem CATALINA_HOME/endorsed exists.
+if not exist "%CATALINA_HOME%\endorsed" goto gotEndorseddir
+set "JAVA_ENDORSED_DIRS=%CATALINA_HOME%\endorsed"
+:gotEndorseddir
+
+rem Don't override _RUNJAVA if the user has set it previously
+if not "%_RUNJAVA%" == "" goto gotRunJava
+rem Set standard command for invoking Java.
+rem Also note the quoting as JRE_HOME may contain spaces.
+set _RUNJAVA="%JRE_HOME%\bin\java.exe"
+:gotRunJava
+
+rem Don't override _RUNJDB if the user has set it previously
+rem Also note the quoting as JAVA_HOME may contain spaces.
+if not "%_RUNJDB%" == "" goto gotRunJdb
+set _RUNJDB="%JAVA_HOME%\bin\jdb.exe"
+:gotRunJdb
+if errorlevel 1 goto end
+
+rem Add on extra jar file to CLASSPATH
+rem Note that there are no quotes as we do not want to introduce random
+rem quotes into the CLASSPATH
+if "%CLASSPATH%" == "" goto emptyClasspath
+set "CLASSPATH=%CLASSPATH%;"
+:emptyClasspath
+set "CLASSPATH=%CLASSPATH%%WINEGROWER_HOME%\lib\*"
+
+if not "%WINEGROWER_TMPDIR%" == "" goto gotTmpdir
+set "WINEGROWER_TMPDIR=%WINEGROWER_BASE%\temp"
+:gotTmpdir
+
+if not "%JSSE_OPTS%" == "" goto gotJsseOpts
+set JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048"
+:gotJsseOpts
+set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"
+
+if not "%LOGGING_CONFIG%" == "" goto noJuliConfig
+set LOGGING_CONFIG="-Dwinegrower.script.nologgingconfig"
+if not exist "%WINEGROWER_BASE%\conf\logging.properties" goto noJuliConfig
+set LOGGING_CONFIG=-Djava.util.logging.config.file="%WINEGROWER_BASE%\conf\logging.properties"
+:noJuliConfig
+
+if not "%LOGGING_MANAGER%" == "" goto noJuliManager
+set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
+:noJuliManager
+
+rem Configure JAVA 9 specific start-up parameters
+rem TODO: set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.lang=ALL-UNNAMED"
+rem TODO: set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED"
+
+rem ----- Execute The Requested Command ---------------------------------------
+
+echo Using WINEGROWER_BASE: "%WINEGROWER_BASE%"
+echo Using WINEGROWER_HOME: "%WINEGROWER_HOME%"
+echo Using WINEGROWER_TMPDIR: "%WINEGROWER_TMPDIR%"
+if ""%1"" == ""debug"" goto use_jdk
+echo Using JRE_HOME: "%JRE_HOME%"
+goto java_dir_displayed
+:use_jdk
+echo Using JAVA_HOME: "%JAVA_HOME%"
+:java_dir_displayed
+echo Using CLASSPATH: "%CLASSPATH%"
+
+set _EXECJAVA=%_RUNJAVA%
+set MAINCLASS=${main}
+set ACTION=start
+set SECURITY_POLICY_FILE=
+set DEBUG_OPTS=
+set JPDA=
+
+if not ""%1"" == ""jpda"" goto noJpda
+set JPDA=jpda
+if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
+set JPDA_TRANSPORT=dt_socket
+:gotJpdaTransport
+if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
+set JPDA_ADDRESS=localhost:8000
+:gotJpdaAddress
+if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
+set JPDA_SUSPEND=n
+:gotJpdaSuspend
+if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
+set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
+:gotJpdaOpts
+shift
+:noJpda
+
+if ""%1"" == ""debug"" goto doDebug
+if ""%1"" == ""run"" goto doRun
+if ""%1"" == ""start"" goto doStart
+if ""%1"" == ""stop"" goto doStop
+
+echo Usage: WINEGROWER ( commands ... )
+echo commands:
+echo run Start WINEGROWER in the current window
+echo start Start WINEGROWER in a separate window
+echo stop Stop WINEGROWER
+goto end
+
+:doDebug
+shift
+set _EXECJAVA=%_RUNJDB%
+set DEBUG_OPTS=-sourcepath "%WINEGROWER_HOME%\..\..\java"
+if not ""%1"" == ""-security"" goto execCmd
+shift
+echo Using Security Manager
+set "SECURITY_POLICY_FILE=%WINEGROWER_BASE%\conf\WINEGROWER.policy"
+goto execCmd
+
+:doRun
+goto execCmd
+
+:doStart
+shift
+if "%TITLE%" == "" set TITLE=Winegrower
+set _EXECJAVA=start "%TITLE%" %_RUNJAVA%
+goto execCmd
+
+:doStop
+shift
+set ACTION=stop
+set WINEGROWER_OPTS=
+goto execCmd
+
+:execCmd
+rem Get remaining unshifted command line arguments and save them in the
+set CMD_LINE_ARGS=
+:setArgs
+if ""%1""=="""" goto doneSetArgs
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setArgs
+:doneSetArgs
+
+rem Execute Java with the applicable properties
+if not "%JPDA%" == "" goto doJpda
+if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
+%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %WINEGROWER_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -DWINEGROWER.base="%WINEGROWER_BASE%" -DWINEGROWER.home="%WINEGROWER_HOME%" -Djava.io.tmpdir="%WINEGROWER_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+:doSecurity
+%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %WINEGROWER_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -DWINEGROWER.base="%WINEGROWER_BASE%" -DWINEGROWER.home="%WINEGROWER_HOME%" -Djava.io.tmpdir="%WINEGROWER_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+:doJpda
+if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
+%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %JPDA_OPTS% %WINEGROWER_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -DWINEGROWER.base="%WINEGROWER_BASE%" -DWINEGROWER.home="%WINEGROWER_HOME%" -Djava.io.tmpdir="%WINEGROWER_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+:doSecurityJpda
+%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %JPDA_OPTS% %WINEGROWER_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -DWINEGROWER.base="%WINEGROWER_BASE%" -DWINEGROWER.home="%WINEGROWER_HOME%" -Djava.io.tmpdir="%WINEGROWER_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
+goto end
+
+:end
\ No newline at end of file
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/main/resources/bin/winegrower.sh b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/resources/bin/winegrower.sh
new file mode 100644
index 0000000..e907c9d
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/main/resources/bin/winegrower.sh
@@ -0,0 +1,486 @@
+#!/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.
+
+#
+# This script is forked from Apache Tomcat
+#
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false
+darwin=false
+os400=false
+hpux=false
+case "`uname`" in
+CYGWIN*) cygwin=true;;
+Darwin*) darwin=true;;
+OS400*) os400=true;;
+HP-UX*) hpux=true;;
+esac
+
+# resolve links - $0 may be a softlink
+PRG="$0"
+
+while [ -h "$PRG" ]; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`/"$link"
+ fi
+done
+
+# Get standard environment variables
+PRGDIR=`dirname "$PRG"`
+
+# Only set WINEGROWER_HOME if not already set
+[ -z "$WINEGROWER_HOME" ] && WINEGROWER_HOME=`cd "$PRGDIR/.." >/dev/null; pwd`
+
+# Copy WINEGROWER_BASE from WINEGROWER_HOME if not already set
+[ -z "$WINEGROWER_BASE" ] && WINEGROWER_BASE="$WINEGROWER_HOME"
+
+# Ensure that any user defined CLASSPATH variables are not used on startup,
+# but allow them to be specified in setenv.sh, in rare case when it is needed.
+CLASSPATH=
+
+if [ -r "$WINEGROWER_BASE/bin/setenv.sh" ]; then
+ . "$WINEGROWER_BASE/bin/setenv.sh"
+elif [ -r "$WINEGROWER_HOME/bin/setenv.sh" ]; then
+ . "$WINEGROWER_HOME/bin/setenv.sh"
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$JRE_HOME" ] && JRE_HOME=`cygpath --unix "$JRE_HOME"`
+ [ -n "$WINEGROWER_HOME" ] && WINEGROWER_HOME=`cygpath --unix "$WINEGROWER_HOME"`
+ [ -n "$WINEGROWER_BASE" ] && WINEGROWER_BASE=`cygpath --unix "$WINEGROWER_BASE"`
+ [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# Ensure that neither WINEGROWER_HOME nor WINEGROWER_BASE contains a colon
+# as this is used as the separator in the classpath and Java provides no
+# mechanism for escaping if the same character appears in the path.
+case $WINEGROWER_HOME in
+ *:*) echo "Using WINEGROWER_HOME: $WINEGROWER_HOME";
+ echo "Unable to start as WINEGROWER_HOME contains a colon (:) character";
+ exit 1;
+esac
+case $WINEGROWER_BASE in
+ *:*) echo "Using WINEGROWER_BASE: $WINEGROWER_BASE";
+ echo "Unable to start as WINEGROWER_BASE contains a colon (:) character";
+ exit 1;
+esac
+
+# For OS400
+if $os400; then
+ # Set job priority to standard for interactive (interactive - 6) by using
+ # the interactive priority - 6, the helper threads that respond to requests
+ # will be running at the same priority as interactive jobs.
+ COMMAND='chgjob job('$JOBNAME') runpty(6)'
+ system $COMMAND
+
+ # Enable multi threading
+ export QIBM_MULTI_THREADED=Y
+fi
+
+# Get standard Java environment variables
+# Make sure prerequisite environment variables are set
+if [ -z "$JAVA_HOME" -a -z "$JRE_HOME" ]; then
+ if $darwin; then
+ # Bugzilla 54390
+ if [ -x '/usr/libexec/java_home' ] ; then
+ export JAVA_HOME=`/usr/libexec/java_home`
+ # Bugzilla 37284 (reviewed).
+ elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then
+ export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
+ fi
+ else
+ JAVA_PATH=`which java 2>/dev/null`
+ if [ "x$JAVA_PATH" != "x" ]; then
+ JAVA_PATH=`dirname $JAVA_PATH 2>/dev/null`
+ JRE_HOME=`dirname $JAVA_PATH 2>/dev/null`
+ fi
+ if [ "x$JRE_HOME" = "x" ]; then
+ # XXX: Should we try other locations?
+ if [ -x /usr/bin/java ]; then
+ JRE_HOME=/usr
+ fi
+ fi
+ fi
+ if [ -z "$JAVA_HOME" -a -z "$JRE_HOME" ]; then
+ echo "Neither the JAVA_HOME nor the JRE_HOME environment variable is defined"
+ echo "At least one of these environment variable is needed to run this program"
+ exit 1
+ fi
+fi
+if [ -z "$JAVA_HOME" -a "$1" = "debug" ]; then
+ echo "JAVA_HOME should point to a JDK in order to run in debug mode."
+ exit 1
+fi
+if [ -z "$JRE_HOME" ]; then
+ JRE_HOME="$JAVA_HOME"
+fi
+
+# If we're running under jdb, we need a full jdk.
+if [ "$1" = "debug" ] ; then
+ if [ "$os400" = "true" ]; then
+ if [ ! -x "$JAVA_HOME"/bin/java -o ! -x "$JAVA_HOME"/bin/javac ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly"
+ echo "This environment variable is needed to run this program"
+ echo "NB: JAVA_HOME should point to a JDK not a JRE"
+ exit 1
+ fi
+ else
+ if [ ! -x "$JAVA_HOME"/bin/java -o ! -x "$JAVA_HOME"/bin/jdb -o ! -x "$JAVA_HOME"/bin/javac ]; then
+ echo "The JAVA_HOME environment variable is not defined correctly"
+ echo "This environment variable is needed to run this program"
+ echo "NB: JAVA_HOME should point to a JDK not a JRE"
+ exit 1
+ fi
+ fi
+fi
+
+# Set standard commands for invoking Java, if not already set.
+if [ -z "$_RUNJAVA" ]; then
+ _RUNJAVA="$JRE_HOME"/bin/java
+fi
+if [ "$os400" != "true" ]; then
+ if [ -z "$_RUNJDB" ]; then
+ _RUNJDB="$JAVA_HOME"/bin/jdb
+ fi
+fi
+
+# Add on extra jar files to CLASSPATH
+if [ ! -z "$CLASSPATH" ] ; then
+ CLASSPATH="$CLASSPATH":
+fi
+if [ "$WINEGROWER_HOME" != "$WINEGROWER_BASE" ]; then
+ for i in "$WINEGROWER_BASE/lib/"*.jar ; do
+ if [ -z "$CLASSPATH" ] ; then
+ CLASSPATH="$i"
+ else
+ CLASSPATH="$i:$CLASSPATH"
+ fi
+ done
+fi
+for i in "$WINEGROWER_HOME/lib/"*.jar ; do
+ if [ -z "$CLASSPATH" ] ; then
+ CLASSPATH="$i"
+ else
+ CLASSPATH="$i:$CLASSPATH"
+ fi
+done
+
+if [ -z "$WINEGROWER_OUT" ] ; then
+ WINEGROWER_OUT="$WINEGROWER_BASE"/logs/winegrower.out
+fi
+
+if [ -z "$WINEGROWER_TMPDIR" ] ; then
+ # Define the java.io.tmpdir to use for WINEGROWER
+ WINEGROWER_TMPDIR="$WINEGROWER_BASE"/temp
+fi
+
+# Bugzilla 37848: When no TTY is available, don't output to console
+have_tty=0
+if [ "`tty`" != "not a tty" ]; then
+ have_tty=1
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ JAVA_HOME=`cygpath --absolute --windows "$JAVA_HOME"`
+ JRE_HOME=`cygpath --absolute --windows "$JRE_HOME"`
+ WINEGROWER_HOME=`cygpath --absolute --windows "$WINEGROWER_HOME"`
+ WINEGROWER_BASE=`cygpath --absolute --windows "$WINEGROWER_BASE"`
+ WINEGROWER_TMPDIR=`cygpath --absolute --windows "$WINEGROWER_TMPDIR"`
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+if [ -z "$JSSE_OPTS" ] ; then
+ JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048"
+fi
+JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS"
+
+# Set juli LogManager config file if it is present and an override has not been issued
+if [ -z "$LOGGING_CONFIG" ]; then
+ if [ -r "$WINEGROWER_BASE"/conf/logging.properties ]; then
+ LOGGING_CONFIG="-Djava.util.logging.config.file=$WINEGROWER_BASE/conf/logging.properties"
+ else
+ # Bugzilla 45585
+ LOGGING_CONFIG="-Dwinegrower.script.nologgingconfig"
+ fi
+fi
+
+if [ -z "$LOGGING_MANAGER" ]; then
+ LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager"
+fi
+
+# Set UMASK unless it has been overridden
+if [ -z "$UMASK" ]; then
+ UMASK="0027"
+fi
+umask $UMASK
+
+# Uncomment the following line to make the umask available when using the
+# org.apache.tomcat.security.SecurityListener
+#JAVA_OPTS="$JAVA_OPTS -Dorg.apache.tomcat.security.SecurityListener.UMASK=`umask`"
+
+if [ -z "$USE_NOHUP" ]; then
+ if $hpux; then
+ USE_NOHUP="true"
+ else
+ USE_NOHUP="false"
+ fi
+fi
+unset _NOHUP
+if [ "$USE_NOHUP" = "true" ]; then
+ _NOHUP=nohup
+fi
+
+if [ -z "$WINEGROWER_PID" ]; then
+ WINEGROWER_PID="$WINEGROWER_BASE"/conf/winegrower.pid
+fi
+# ----- Execute The Requested Command -----------------------------------------
+
+# Bugzilla 37848: only output this if we have a TTY
+if [ $have_tty -eq 1 ]; then
+ echo "Using WINEGROWER_BASE: $WINEGROWER_BASE"
+ echo "Using WINEGROWER_HOME: $WINEGROWER_HOME"
+ echo "Using WINEGROWER_TMPDIR: $WINEGROWER_TMPDIR"
+ echo "Using JRE_HOME: $JRE_HOME"
+ echo "Using CLASSPATH: $CLASSPATH"
+ if [ ! -z "$WINEGROWER_PID" ]; then
+ echo "Using WINEGROWER_PID: $WINEGROWER_PID"
+ fi
+fi
+
+if [ "$1" = "jpda" ] ; then
+ if [ -z "$JPDA_TRANSPORT" ]; then
+ JPDA_TRANSPORT="dt_socket"
+ fi
+ if [ -z "$JPDA_ADDRESS" ]; then
+ JPDA_ADDRESS="localhost:8000"
+ fi
+ if [ -z "$JPDA_SUSPEND" ]; then
+ JPDA_SUSPEND="n"
+ fi
+ if [ -z "$JPDA_OPTS" ]; then
+ JPDA_OPTS="-agentlib:jdwp=transport=$JPDA_TRANSPORT,server=y,address=$JPDA_ADDRESS,suspend=$JPDA_SUSPEND"
+ fi
+ WINEGROWER_OPTS="$JPDA_OPTS $WINEGROWER_OPTS"
+ shift
+fi
+
+WINEGROWER_LOG4J2_PATH="$WINEGROWER_BASE"/conf/log4j2.xml
+if [ -f "$WINEGROWER_LOG4J2_PATH" ]; then
+ if $cygwin; then
+ WINEGROWER_LOG4J2_PATH=`cygpath --absolute --windows "$WINEGROWER_LOG4J2_PATH"`
+ fi
+ WINEGROWER_OPTS="$WINEGROWER_OPTS "-Dlog4j.configurationFile=\"$WINEGROWER_LOG4J2_PATH\"""
+fi
+
+if [ "$1" = "run" ]; then
+
+ shift
+ eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $WINEGROWER_OPTS \
+ -classpath "\"$CLASSPATH\"" \
+ -Dwinegrower.base="\"$WINEGROWER_BASE\"" \
+ -Dwinegrower.home="\"$WINEGROWER_HOME\"" \
+ -Dkaraf.home="\"$WINEGROWER_HOME\"" \
+ -Dkaraf.base="\"$WINEGROWER_BASE\"" \
+ -Djava.io.tmpdir="\"$WINEGROWER_TMPDIR\"" \
+ ${main} "$WINEGROWER_ARGS" "$@"
+
+elif [ "$1" = "start" ] ; then
+
+ if [ ! -z "$WINEGROWER_PID" ]; then
+ if [ -f "$WINEGROWER_PID" ]; then
+ if [ -s "$WINEGROWER_PID" ]; then
+ echo "Existing PID file found during start."
+ if [ -r "$WINEGROWER_PID" ]; then
+ PID=`cat "$WINEGROWER_PID"`
+ ps -p $PID >/dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ echo "Winegrower appears to still be running with PID $PID. Start aborted."
+ echo "If the following process is not a Winegrower process, remove the PID file and try again:"
+ ps -f -p $PID
+ exit 1
+ else
+ echo "Removing/clearing stale PID file."
+ rm -f "$WINEGROWER_PID" >/dev/null 2>&1
+ if [ $? != 0 ]; then
+ if [ -w "$WINEGROWER_PID" ]; then
+ cat /dev/null > "$WINEGROWER_PID"
+ else
+ echo "Unable to remove or clear stale PID file. Start aborted."
+ exit 1
+ fi
+ fi
+ fi
+ else
+ echo "Unable to read PID file. Start aborted."
+ exit 1
+ fi
+ else
+ rm -f "$WINEGROWER_PID" >/dev/null 2>&1
+ if [ $? != 0 ]; then
+ if [ ! -w "$WINEGROWER_PID" ]; then
+ echo "Unable to remove or write to empty PID file. Start aborted."
+ exit 1
+ fi
+ fi
+ fi
+ fi
+ fi
+
+ shift
+ touch "$WINEGROWER_OUT"
+ eval $_NOHUP "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $WINEGROWER_OPTS \
+ -classpath "\"$CLASSPATH\"" \
+ -Dwinegrower.base="\"$WINEGROWER_BASE\"" \
+ -Dwinegrower.home="\"$WINEGROWER_HOME\"" \
+ -Djava.io.tmpdir="\"$WINEGROWER_TMPDIR\"" \
+ ${main} "$WINEGROWER_ARGS" "$@" \
+ >> "$WINEGROWER_OUT" 2>&1 "&"
+
+ if [ ! -z "$WINEGROWER_PID" ]; then
+ echo $! > "$WINEGROWER_PID"
+ fi
+
+ echo "Winegrower started."
+
+elif [ "$1" = "stop" ] ; then
+
+ shift
+
+ SLEEP=15
+ if [ ! -z "$1" ]; then
+ echo $1 | grep "[^0-9]" >/dev/null 2>&1
+ if [ $? -gt 0 ]; then
+ SLEEP=$1
+ shift
+ fi
+ fi
+
+ FORCE=0
+ if [ "$1" = "-force" ]; then
+ shift
+ FORCE=1
+ fi
+
+ if [ ! -z "$WINEGROWER_PID" ]; then
+ if [ -f "$WINEGROWER_PID" ]; then
+ if [ -s "$WINEGROWER_PID" ]; then
+ kill -15 `cat "$WINEGROWER_PID"` >/dev/null 2>&1
+ if [ $? -gt 0 ]; then
+ echo "PID file found but no matching process was found. Stop aborted."
+ exit 1
+ fi
+ else
+ echo "PID file is empty and has been ignored."
+ fi
+ else
+ echo "\$WINEGROWER_PID was set but the specified file does not exist. Is Winegrower running? Stop aborted."
+ exit 1
+ fi
+ fi
+
+ if [ ! -z "$WINEGROWER_PID" ]; then
+ if [ -f "$WINEGROWER_PID" ]; then
+ while [ $SLEEP -ge 0 ]; do
+ kill -15 `cat "$WINEGROWER_PID"` >/dev/null 2>&1
+ if [ $? -gt 0 ]; then
+ rm -f "$WINEGROWER_PID" >/dev/null 2>&1
+ if [ $? != 0 ]; then
+ if [ -w "$WINEGROWER_PID" ]; then
+ cat /dev/null > "$WINEGROWER_PID"
+ # If Winegrower has stopped don't try and force a stop with an empty PID file
+ FORCE=0
+ else
+ echo "The PID file could not be removed or cleared."
+ fi
+ fi
+ echo "Winegrower stopped."
+ break
+ fi
+ if [ $SLEEP -gt 0 ]; then
+ sleep 1
+ fi
+ if [ $SLEEP -eq 0 ]; then
+ echo "Winegrower did not stop in time."
+ if [ $FORCE -eq 0 ]; then
+ echo "PID file was not removed."
+ fi
+ echo "To aid diagnostics a thread dump has been written to standard out."
+ kill -3 `cat "$WINEGROWER_PID"`
+ fi
+ SLEEP=`expr $SLEEP - 1 `
+ done
+ fi
+ fi
+
+ KILL_SLEEP_INTERVAL=15
+ if [ $FORCE -eq 1 ]; then
+ if [ -z "$WINEGROWER_PID" ]; then
+ echo "Kill failed: \$WINEGROWER_PID not set"
+ else
+ if [ -f "$WINEGROWER_PID" ]; then
+ PID=`cat "$WINEGROWER_PID"`
+ echo "Killing Winegrower with the PID: $PID"
+ kill -9 $PID
+ while [ $KILL_SLEEP_INTERVAL -ge 0 ]; do
+ kill -0 `cat "$WINEGROWER_PID"` >/dev/null 2>&1
+ if [ $? -gt 0 ]; then
+ rm -f "$WINEGROWER_PID" >/dev/null 2>&1
+ if [ $? != 0 ]; then
+ if [ -w "$WINEGROWER_PID" ]; then
+ cat /dev/null > "$WINEGROWER_PID"
+ else
+ echo "The PID file could not be removed."
+ fi
+ fi
+ echo "The Winegrower process has been killed."
+ break
+ fi
+ if [ $KILL_SLEEP_INTERVAL -gt 0 ]; then
+ sleep 1
+ fi
+ KILL_SLEEP_INTERVAL=`expr $KILL_SLEEP_INTERVAL - 1 `
+ done
+ if [ $KILL_SLEEP_INTERVAL -lt 0 ]; then
+ echo "Winegrower has not been killed completely yet. The process might be waiting on some system call or might be UNINTERRUPTIBLE."
+ fi
+ fi
+ fi
+ fi
+
+else
+
+ echo "Usage: WINEGROWER.sh ( commands ... )"
+ echo "commands:"
+ echo " jpda start Start WINEGROWER under JPDA debugger"
+ echo " run Start WINEGROWER in the current window"
+ echo " start Start WINEGROWER in a separate window"
+ echo " stop Stop WINEGROWER, waiting up t/o 15 seconds for the process to end"
+ echo " stop n Stop WINEGROWER, waiting up to n seconds for the process to end"
+ echo " stop -force Stop WINEGROWER, wait up to 15 seconds and then use kill -KILL if still running"
+ echo " stop n -force Stop WINEGROWER, wait up to n seconds and then use kill -KILL if still running"
+ echo "Note: Waiting for the process to end and use of the -force option require that \$WINEGROWER_PID is defined"
+ exit 1
+
+fi
\ No newline at end of file
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/test/java/org/apache/winegrower/extension/build/common/BuildTest.java b/winegrower-extension/winegrower-build/winegrower-build-common/src/test/java/org/apache/winegrower/extension/build/common/BuildTest.java
new file mode 100644
index 0000000..47e1f21
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/test/java/org/apache/winegrower/extension/build/common/BuildTest.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.extension.build.common;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.list;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+import static org.apache.xbean.finder.util.Files.toFile;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.winegrower.Ripener;
+import org.junit.jupiter.api.Test;
+import org.osgi.framework.Bundle;
+
+class BuildTest {
+ @Test
+ void build() throws IOException {
+ final String junitMarker = Test.class.getName().replace('.', '/') + ".class";
+ final File junitApi = toFile(Thread.currentThread().getContextClassLoader().getResource(junitMarker));
+ final String osgiCoreMarker = Bundle.class.getName().replace('.', '/') + ".class";
+ final File osgiCore = toFile(Thread.currentThread().getContextClassLoader().getResource(osgiCoreMarker));
+
+ final File output = new File("target/buildtest/build.jar");
+ if (output.exists()) {
+ output.delete();
+ }
+ final File workDir = new File(output.getParentFile(), "work");
+ new Build(new Build.Configuration(
+ workDir,
+ new File("src/test/resources/build"),
+ "test-art",
+ asList(junitApi, osgiCore),
+ singletonList("zip"),
+ Ripener.class.getName(),
+ "bin", "conf", false, false
+ )).run();
+ final File distro = new File(workDir, "test-art-winegrower-distribution.zip");
+ assertTrue(distro.exists());
+ final List<String> entries;
+ try (final JarFile files = new JarFile(distro)) {
+ entries = list(files.entries()).stream().map(JarEntry::getName).collect(toList());
+ }
+ assertEquals(11, entries.size());
+ assertTrue(entries.contains("test-art-winegrower-distribution/bin/winegrower.sh"));
+ assertTrue(entries.contains("test-art-winegrower-distribution/bin/setenv.sh"));
+ assertTrue(entries.contains("test-art-winegrower-distribution/lib/org.osgi.core-6.0.0.jar"));
+ assertTrue(entries.contains("test-art-winegrower-distribution/lib/junit-jupiter-api-5.3.1.jar"));
+ }
+}
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/test/java/org/apache/winegrower/extension/build/common/FatJarTest.java b/winegrower-extension/winegrower-build/winegrower-build-common/src/test/java/org/apache/winegrower/extension/build/common/FatJarTest.java
new file mode 100644
index 0000000..b41c05b
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/test/java/org/apache/winegrower/extension/build/common/FatJarTest.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.extension.build.common;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.list;
+import static java.util.stream.Collectors.toList;
+import static org.apache.xbean.finder.util.Files.toFile;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.junit.jupiter.api.Test;
+import org.osgi.framework.Bundle;
+
+class FatJarTest {
+ @Test
+ void fatJar() throws IOException {
+ final String junitMarker = Test.class.getName().replace('.', '/') + ".class";
+ final File junitApi = toFile(Thread.currentThread().getContextClassLoader().getResource(junitMarker));
+ final String osgiCoreMarker = Bundle.class.getName().replace('.', '/') + ".class";
+ final File osgiCore = toFile(Thread.currentThread().getContextClassLoader().getResource(osgiCoreMarker));
+
+ final File output = new File("target/farjartest/fatJar.jar");
+ if (output.exists()) {
+ output.delete();
+ }
+ new FatJar(new FatJar.Configuration(
+ asList(junitApi, osgiCore),
+ output
+ )).run();
+ assertTrue(output.exists());
+ final List<String> entries;
+ try (final JarFile files = new JarFile(output)) {
+ entries = list(files.entries()).stream().map(JarEntry::getName).collect(toList());
+ }
+ assertTrue(entries.size() > 500); // 503 when writing this test
+ // ensure junit and osgi-core are here by testing a few known classes
+ assertTrue(entries.contains("org/junit/jupiter/api/AfterAll.class"));
+ assertTrue(entries.contains("org/osgi/framework/FrameworkUtil.class"));
+ // ensure fatjar meta are here
+ assertTrue(entries.contains("WINEGROWER-INF/index.properties"));
+ assertTrue(entries.contains("WINEGROWER-INF/manifests.properties"));
+ }
+}
diff --git a/winegrower-extension/winegrower-build/winegrower-build-common/src/test/resources/build/bin/setenv.sh b/winegrower-extension/winegrower-build/winegrower-build-common/src/test/resources/build/bin/setenv.sh
new file mode 100644
index 0000000..81f90a2
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-build-common/src/test/resources/build/bin/setenv.sh
@@ -0,0 +1,18 @@
+#!/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.
+
+echo "Starting"
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/pom.xml b/winegrower-extension/winegrower-build/winegrower-maven-plugin/pom.xml
index 72a214c..be2e44e 100644
--- a/winegrower-extension/winegrower-build/winegrower-maven-plugin/pom.xml
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/pom.xml
@@ -59,6 +59,11 @@
<artifactId>winegrower-build-common</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.maven.shared</groupId>
+ <artifactId>maven-dependency-tree</artifactId>
+ <version>3.0.1</version>
+ </dependency>
</dependencies>
<build>
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc
index c6fc818..12df619 100644
--- a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/asciidoc/index.adoc
@@ -1,5 +1,20 @@
= Apache Winegrower Maven Plugin
+Apache Winegrower Maven plugin provides the elementary tasks to develop and package your application depending your need.
+
+Here is how to declare it:
+
+[source,xml]
+----
+<plugin>
+ <groupId>org.apache.winegrower</groupId>
+ <artifactId>winegrower-maven-plugin</artifactId>
+ <version>${winegrower.version}</version>
+</plugin>
+----
+
+== Start a dev instance (a.k.a. `Pour`)
+
Apache Winegrower Maven plugin allows to start an instance very easily thanks to `pour` command:
[source,sh]
@@ -7,7 +22,7 @@
mvn winegrower:pour
----
-== Configuration
+=== Configuration
[cols="e,m,m,m,a",headers]
|===
@@ -22,7 +37,7 @@
|waitOnSystemIn|boolean|winegrower.waitOnSystemIn|${project.build.directory}/winegrower/workdir|should the mojo wait you enter any character to exist, it is useful to disable it if you use the Karaf shell which reads `System.in` as well.
|===
-== Example
+=== Example
[source,xml]
----
@@ -37,4 +52,71 @@
<waitOnSystemIn>false</waitOnSystemIn> <!-- cause of the shell -->
</configuration>
</plugin>
-----
\ No newline at end of file
+----
+
+== Create a fatjar
+
+Creating a fatjar is a common need. For OSGi it becomes quite an issue
+since you need to keep each `MANIFEST.MF` as it since some values can't become plural.
+This is the main blocker making the `maven-shade-plugin` and `maven-assembly-plugin` quite helpless
+on that task.
+
+To solve that, Winegrower provides a `fatjar` goal which will generate an index of each shaded jars
+and keep their manifests as it and the runtime knows how to read them back.
+
+[source,sh]
+----
+mvn winegrower:fatjar
+----
+
+=== Configuration
+
+[cols="e,m,m,m,a",headers]
+|===
+|Name|Type|Property|Default|Description
+|attach|boolean|winegrower.attach|true|should the built jar be attached (installed)
+|buildArtifact|File|winegrower.buildArtifact|${project.build.directory}/${project.build.finalName}.${project.packaging}.|Path of the project artifact if any.
+|classifier|String|winegrower.classifier|fatjar|The classifier to use if the produced artifact is attached.
+|includeScopes|Collection<String>|winegrower.includeScopes|provided,compile,runtime|The scopes included in the produced artifact.
+|output|File|winegrower.output|${project.build.directory}/${project.artifactId}-fatjar.jar|Where the fatjar is produced.
+|===
+
+== Create a distribution
+
+Since Winegrower does not require an OSGi runtime anymore, it also provide a `distribution` goal
+which will bundle your application as a standard java application with a `lib/` folder
+and scripts in `bin` to start/stop the application.
+
+The scripts are highly inspired from Apache Tomcat ones so if you are familiar with this server
+you shouldn't be lost.
+
+[source,sh]
+----
+mvn winegrower:distribution
+----
+
+Then once the distributino unzipped you can start using:
+
+[source,sh]
+----
+./bin/wingrower.sh run
+----
+
+=== Configuration
+
+[cols="e,m,m,m,a",headers]
+|===
+|Name|Type|Property|Default|Description
+|attach|boolean|winegrower.attach|true|should the built distributions be attached (installed)
+|buildArtifact|File|winegrower.buildArtifact|${project.build.directory}/${project.build.finalName}.${project.packaging}.|Path of the project artifact if any.
+|classifier|String|winegrower.classifier|fatjar-%s.|The classifier to use if the produced artifact is attached. Note it is a pattern (`String.format`) taking the format as parameter.
+|includeScopes|Collection<String>|winegrower.includeScopes|provided,compile,runtime|The scopes included in the produced artifact.
+|conf|String|winegrower.conf|src/main/winegrower/conf|Path synchronized with the distribution conf folder.
+|bin|String|winegrower.bin|src/main/winegrower/bin|Path synchronized with the distribution bin folder.
+|formats|Collection<String>|winegrower.formats|zip|Distribution formats, `zip` and `tar.gz` are supported.
+|keepExplodedFolder|boolean|winegrower.keepExplodedFolder|false|Should the distribution work directory be kept in the build directory.
+|libs|Collection<String>|winegrower.libs|-|List of maven coordinates (`group:artifact:version[?transitive]`) to include in the distribution even if not visible as dependency.
+|main|String|winegrower.main|org.apache.winegrower.Ripener|The main to run when starting the distribution.
+|skipArchiveRootFolder|boolean|winegrower.skipArchiveRootFolder|false|Should the distribution keep a root folder.
+|workDir|File|winegrower.workDir|${project.build.directory}/${project.artifactId}-distribution|Where the distribution is built during the build.
+|===
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/DistributionMojo.java b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/DistributionMojo.java
new file mode 100644
index 0000000..b5890f9
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/DistributionMojo.java
@@ -0,0 +1,217 @@
+/**
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.extension.build.maven;
+
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
+import static org.apache.maven.plugins.annotations.ResolutionScope.RUNTIME_PLUS_SYSTEM;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.DefaultDependencyResolutionRequest;
+import org.apache.maven.project.DependencyResolutionException;
+import org.apache.maven.project.DependencyResolutionRequest;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.apache.maven.project.ProjectDependenciesResolver;
+import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
+import org.apache.winegrower.extension.build.common.Build;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.graph.DependencyNode;
+import org.eclipse.aether.graph.DependencyVisitor;
+import org.eclipse.aether.impl.ArtifactResolver;
+import org.eclipse.aether.repository.LocalRepositoryManager;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+
+@Mojo(name = "distribution", requiresDependencyResolution = RUNTIME_PLUS_SYSTEM)
+public class DistributionMojo extends AbstractMojo {
+ @Parameter(defaultValue = "${project}", readonly = true, required = true)
+ private MavenProject project;
+
+ @Parameter(defaultValue = "provided,compile,runtime", property = "winegrower.includeScopes")
+ private Collection<String> includeScopes;
+
+ @Parameter(defaultValue = "${project.build.directory}/${project.artifactId}-distribution", property = "winegrower.workDir")
+ private File workDir;
+
+ @Parameter(defaultValue = "zip", property = "winegrower.formats")
+ private Collection<String> formats;
+
+ @Parameter(defaultValue = "org.apache.winegrower.Ripener", property = "winegrower.main")
+ private String main;
+
+ @Parameter(property = "winegrower.bin", defaultValue = "src/main/winegrower/bin")
+ private String bin;
+
+ @Parameter(property = "winegrower.conf", defaultValue = "src/main/winegrower/conf")
+ private String conf;
+
+ @Parameter(property = "winegrower.libs")
+ private Collection<String> libs;
+
+ @Parameter(property = "winegrower.no-root", defaultValue = "false")
+ private boolean skipArchiveRootFolder;
+
+ @Parameter(property = "winegrower.keep-exploded-folder", defaultValue = "false")
+ private boolean keepExplodedFolder;
+
+ @Parameter(defaultValue = "true", property = "winegrower.attach")
+ private boolean attach;
+
+ @Parameter(defaultValue = "fatjar-%s", property = "winegrower.classifier")
+ private String classifier;
+
+ @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}", property = "winegrower.buildArtifact")
+ private File buildArtifact;
+
+ @Component
+ private MavenProjectHelper helper;
+
+ @Component
+ private ArtifactResolver resolver;
+
+ @Component
+ private RepositorySystem repositorySystem;
+
+ @Component
+ private ProjectDependenciesResolver dependenciesResolver;
+
+ @Component
+ private DependencyGraphBuilder graphBuilder;
+
+ @Parameter(defaultValue = "${repositorySystemSession}")
+ private RepositorySystemSession session;
+
+ @Parameter(defaultValue = "${project.remoteProjectRepositories}")
+ private List<RemoteRepository> remoteRepositories;
+
+ @Override
+ public void execute() {
+ new Build(new Build.Configuration(
+ workDir, project.getBasedir(), project.getArtifactId(),
+ collectJars(), formats,
+ main, bin, conf, skipArchiveRootFolder, keepExplodedFolder
+ )).run();
+ if (attach) {
+ formats.forEach(ext -> helper.attachArtifact(
+ project,
+ new File(workDir, project.getArtifactId() + "-winegrower-distribution." + ext),
+ String.format(classifier, ext)));
+ }
+ }
+
+ private Stream<File> collectTransitiveDependencies(final Dependency dependency) {
+ final DependencyResolutionRequest request = new DefaultDependencyResolutionRequest();
+ request.setMavenProject(new MavenProject() {{
+ getDependencies().add(dependency);
+ }});
+ request.setRepositorySession(session);
+ try {
+ final Collection<File> files = new ArrayList<>();
+ dependenciesResolver.resolve(request).getDependencyGraph().accept(new DependencyVisitor() {
+ @Override
+ public boolean visitEnter(final DependencyNode node) {
+ return true;
+ }
+
+ @Override
+ public boolean visitLeave(final DependencyNode node) {
+ final org.eclipse.aether.artifact.Artifact artifact = node.getArtifact();
+ files.add(artifact.getFile());
+ return true;
+ }
+ });
+ return files.stream();
+ } catch (final DependencyResolutionException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ private File resolve(final String group, final String artifact, final String version, final String classifier) {
+ final DefaultArtifact art = new DefaultArtifact(group, artifact, classifier, "jar", version);
+ final ArtifactRequest artifactRequest = new ArtifactRequest().setArtifact(art).setRepositories(remoteRepositories);
+
+ final LocalRepositoryManager lrm = session.getLocalRepositoryManager();
+ art.setFile(new File(lrm.getRepository().getBasedir(), lrm.getPathForLocalArtifact(artifactRequest.getArtifact())));
+
+ try {
+ final ArtifactResult result = repositorySystem.resolveArtifact(session, artifactRequest);
+ if (result.isMissing()) {
+ throw new IllegalStateException("Can't find commons-cli, please add it to the pom.");
+ }
+ return result.getArtifact().getFile();
+ } catch (final ArtifactResolutionException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+ private Stream<File> collectLibs() {
+ return ofNullable(libs)
+ .map(value -> value.stream().flatMap(l -> {
+ final boolean transitive = l.endsWith("?transitive");
+ final String coords = transitive ? l.substring(0, l.length() - "?transitive".length()) : l;
+ final String[] c = coords.split(":");
+ if (c.length < 3 || c.length > 5) {
+ throw new IllegalArgumentException("libs syntax is groupId:artifactId:version[:classifier][:type[?transitive]]");
+ }
+ if (!transitive) {
+ return Stream.of(resolve(c[0], c[1], c[2], c.length == 4 ? c[3] : ""));
+ } else {
+ return collectTransitiveDependencies(new Dependency() {{
+ setGroupId(c[0]);
+ setArtifactId(c[1]);
+ setVersion(c[2]);
+ if (c.length == 4 && !"-".equals(c[3])) {
+ setClassifier(c[3]);
+ }
+ if (c.length == 5) {
+ setType(c[4]);
+ }
+ }});
+ }
+ }).filter(it -> !it.getName().endsWith(".pom")))
+ .orElseGet(Stream::empty);
+ }
+ private Collection<File> collectJars() {
+ return Stream.concat(Stream.concat(
+ collectDependencies(),
+ Stream.of(buildArtifact)),
+ collectLibs())
+ .filter(Objects::nonNull)
+ .filter(File::exists)
+ .collect(toList());
+ }
+
+ private Stream<File> collectDependencies() {
+ return project.getArtifacts().stream()
+ .filter(it -> includeScopes.contains(it.getScope()))
+ .map(Artifact::getFile);
+ }
+}
diff --git a/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/FatJarMojo.java b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/FatJarMojo.java
new file mode 100644
index 0000000..f06b11a
--- /dev/null
+++ b/winegrower-extension/winegrower-build/winegrower-maven-plugin/src/main/java/org/apache/winegrower/extension/build/maven/FatJarMojo.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.winegrower.extension.build.maven;
+
+import static java.util.stream.Collectors.toList;
+import static org.apache.maven.plugins.annotations.ResolutionScope.RUNTIME_PLUS_SYSTEM;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.apache.winegrower.extension.build.common.FatJar;
+
+@Mojo(name = "fatjar", requiresDependencyResolution = RUNTIME_PLUS_SYSTEM)
+public class FatJarMojo extends AbstractMojo {
+ @Parameter(defaultValue = "${project}", readonly = true, required = true)
+ private MavenProject project;
+
+ @Parameter(defaultValue = "provided,compile,runtime", property = "winegrower.includeScopes")
+ private Collection<String> includeScopes;
+
+ @Parameter(defaultValue = "${project.build.directory}/${project.artifactId}-fatjar.jar", property = "winegrower.output")
+ private File output;
+
+ @Parameter(defaultValue = "true", property = "winegrower.attach")
+ private boolean attach;
+
+ @Parameter(defaultValue = "fatjar", property = "winegrower.classifier")
+ private String classifier;
+
+ @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}", property = "winegrower.buildArtifact")
+ private File buildArtifact;
+
+ @Component
+ private MavenProjectHelper helper;
+
+ @Override
+ public void execute() {
+ new FatJar(new FatJar.Configuration(collectJars(), output)).run();
+ if (attach) {
+ helper.attachArtifact(project, output, classifier);
+ }
+ }
+
+ private Collection<File> collectJars() {
+ return Stream.concat(
+ project.getArtifacts().stream()
+ .filter(it -> includeScopes.contains(it.getScope()))
+ .map(Artifact::getFile),
+ Stream.of(buildArtifact))
+ .filter(Objects::nonNull)
+ .filter(File::exists)
+ .collect(toList());
+ }
+}