You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2015/12/18 16:58:24 UTC
karaf git commit: KARAF-222 - Provide karaf:run, karaf:deploy,
karaf:client Maven goals
Repository: karaf
Updated Branches:
refs/heads/master 618a105e7 -> 335620adb
KARAF-222 - Provide karaf:run, karaf:deploy, karaf:client Maven goals
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/335620ad
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/335620ad
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/335620ad
Branch: refs/heads/master
Commit: 335620adb7b1cc92380ec9cba7eb2dbbaa8bb96c
Parents: 618a105
Author: Jean-Baptiste Onofré <jb...@apache.org>
Authored: Fri Dec 18 16:57:37 2015 +0100
Committer: Jean-Baptiste Onofré <jb...@apache.org>
Committed: Fri Dec 18 16:58:09 2015 +0100
----------------------------------------------------------------------
tooling/karaf-maven-plugin/pom.xml | 8 +
.../src/it/test-run-bundle/pom.xml | 65 ++++
.../src/main/java/test/Dummy.java | 25 ++
.../karaf-maven-plugin/src/it/test-run/pom.xml | 52 ++++
.../java/org/apache/karaf/tooling/RunMojo.java | 304 +++++++++++++++++++
.../apache/karaf/tooling/client/ClientMojo.java | 257 ++++++++++++++++
.../apache/karaf/tooling/client/DeployMojo.java | 283 +++++++++++++++++
7 files changed, 994 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/335620ad/tooling/karaf-maven-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/pom.xml b/tooling/karaf-maven-plugin/pom.xml
index 86d6793..f0cc6fa 100644
--- a/tooling/karaf-maven-plugin/pom.xml
+++ b/tooling/karaf-maven-plugin/pom.xml
@@ -182,6 +182,14 @@
<artifactId>org.apache.karaf.shell.console</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.karaf</groupId>
+ <artifactId>org.apache.karaf.main</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.diagnostic</groupId>
+ <artifactId>org.apache.karaf.diagnostic.boot</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
http://git-wip-us.apache.org/repos/asf/karaf/blob/335620ad/tooling/karaf-maven-plugin/src/it/test-run-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-run-bundle/pom.xml b/tooling/karaf-maven-plugin/src/it/test-run-bundle/pom.xml
new file mode 100644
index 0000000..ad15e03
--- /dev/null
+++ b/tooling/karaf-maven-plugin/src/it/test-run-bundle/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>test</groupId>
+ <artifactId>test-run-bundle</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.5.4</version>
+ <inherited>true</inherited>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Package>*</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <version>@pom.version@</version>
+ <executions>
+ <execution>
+ <id>run</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <karafDistribution>mvn:org.apache.karaf/apache-karaf/@pom.version@/zip</karafDistribution>
+ <keepRunning>false</keepRunning>
+ <deployProjectArtifact>true</deployProjectArtifact>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/335620ad/tooling/karaf-maven-plugin/src/it/test-run-bundle/src/main/java/test/Dummy.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-run-bundle/src/main/java/test/Dummy.java b/tooling/karaf-maven-plugin/src/it/test-run-bundle/src/main/java/test/Dummy.java
new file mode 100644
index 0000000..082d7f2
--- /dev/null
+++ b/tooling/karaf-maven-plugin/src/it/test-run-bundle/src/main/java/test/Dummy.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.
+ */
+package test;
+
+public class Dummy {
+
+ // nothing
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/335620ad/tooling/karaf-maven-plugin/src/it/test-run/pom.xml
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/it/test-run/pom.xml b/tooling/karaf-maven-plugin/src/it/test-run/pom.xml
new file mode 100644
index 0000000..58d5905
--- /dev/null
+++ b/tooling/karaf-maven-plugin/src/it/test-run/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>test</groupId>
+ <artifactId>test-run</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <version>@pom.version@</version>
+ <executions>
+ <execution>
+ <id>run</id>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <karafDistribution>mvn:org.apache.karaf/apache-karaf/@pom.version@/zip</karafDistribution>
+ <keepRunning>false</keepRunning>
+ <deployProjectArtifact>false</deployProjectArtifact>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/335620ad/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java
new file mode 100644
index 0000000..449c854
--- /dev/null
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java
@@ -0,0 +1,304 @@
+/*
+ * 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.karaf.tooling;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
+import org.apache.commons.compress.utils.IOUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.karaf.features.BootFinished;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.FeaturesListener;
+import org.apache.karaf.features.FeaturesService;
+import org.apache.karaf.main.Main;
+import org.apache.karaf.tooling.utils.MojoSupport;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+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.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+import java.io.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Run a Karaf instance
+ */
+@Mojo(name = "run", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME)
+public class RunMojo extends MojoSupport {
+
+ /**
+ * Directory containing Karaf container base directory.
+ */
+ @Parameter(defaultValue = "${project.build.directory}/karaf")
+ private File karafDirectory = null;
+
+ /**
+ * Location where to download the Karaf distribution
+ */
+ @Parameter(defaultValue = "mvn:org.apache.karaf/apache-karaf/LATEST/zip")
+ private String karafDistribution = null;
+
+ /**
+ * Define if the project artifact should be deployed in the started container or not
+ */
+ @Parameter(defaultValue = "true")
+ private boolean deployProjectArtifact = true;
+
+ /**
+ * Define if the Karaf container keep running or stop just after the goal execution
+ */
+ @Parameter(defaultValue = "true")
+ private boolean keepRunning = true;
+
+ /**
+ * Define if the Karaf embedded sshd should be started or not
+ */
+ @Parameter(defaultValue = "false")
+ private String startSsh = "false";
+
+ private static final Pattern mvnPattern = Pattern.compile("mvn:([^/ ]+)/([^/ ]+)/([^/ ]*)(/([^/ ]+)(/([^/ ]+))?)?");
+
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ if (karafDirectory.exists()) {
+ getLog().info("Using Karaf container located " + karafDirectory.getAbsolutePath());
+ } else {
+ getLog().info("Extracting Karaf container");
+ try {
+ File karafArchiveFile = resolveFile(karafDistribution);
+ extract(karafArchiveFile, karafDirectory);
+ } catch (Exception e) {
+ throw new MojoFailureException("Can't extract Karaf container", e);
+ }
+ }
+
+ getLog().info("Starting Karaf container");
+ System.setProperty("karaf.home", karafDirectory.getAbsolutePath());
+ System.setProperty("karaf.base", karafDirectory.getAbsolutePath());
+ System.setProperty("karaf.data", karafDirectory.getAbsolutePath() + "/data");
+ System.setProperty("karaf.etc", karafDirectory.getAbsolutePath() + "/etc");
+ System.setProperty("karaf.instances", karafDirectory.getAbsolutePath() + "/instances");
+ System.setProperty("karaf.startLocalConsole", "false");
+ System.setProperty("karaf.startRemoteShell", startSsh);
+ System.setProperty("karaf.lock", "false");
+ Main main = new Main(new String[0]);
+ try {
+ main.launch();
+ while (main.getFramework().getState() != Bundle.ACTIVE) {
+ Thread.sleep(1000);
+ }
+ BundleContext bundleContext = main.getFramework().getBundleContext();
+ Object bootFinished = null;
+ while (bootFinished == null) {
+ Thread.sleep(1000);
+ ServiceReference ref = bundleContext.getServiceReference(BootFinished.class);
+ if (ref != null) {
+ bootFinished = bundleContext.getService(ref);
+ }
+ }
+ deploy(bundleContext);
+ if (keepRunning)
+ main.awaitShutdown();
+ main.destroy();
+ } catch (Throwable e) {
+ throw new MojoExecutionException("Can't start container", e);
+ } finally {
+ System.gc();
+ }
+ }
+
+ private void deploy(BundleContext bundleContext) throws MojoExecutionException {
+ if (deployProjectArtifact) {
+ File artifact = project.getArtifact().getFile();
+ if (artifact != null && artifact.exists()) {
+ if (project.getPackaging().equals("bundle")) {
+ try {
+ Bundle bundle = bundleContext.installBundle(artifact.toURI().toURL().toString());
+ bundle.start();
+ } catch (Exception e) {
+ throw new MojoExecutionException("Can't deploy project artifact in container", e);
+ }
+ } else {
+ throw new MojoExecutionException("Packaging " + project.getPackaging() + " is not supported");
+ }
+ } else {
+ throw new MojoExecutionException("Project artifact doesn't exist");
+ }
+ }
+ }
+
+ public static void extract(File sourceFile, File targetFolder) throws IOException {
+ if (sourceFile.getAbsolutePath().indexOf(".zip") > 0) {
+ extractZipDistribution(sourceFile, targetFolder);
+ } else if (sourceFile.getAbsolutePath().indexOf(".tar.gz") > 0) {
+ extractTarGzDistribution(sourceFile, targetFolder);
+ } else {
+ throw new IllegalStateException("Unknown packaging of distribution; only zip or tar.gz could be handled.");
+ }
+ return;
+ }
+
+ private static void extractTarGzDistribution(File sourceDistribution, File _targetFolder)
+ throws IOException {
+ File uncompressedFile = File.createTempFile("uncompressedTarGz-", ".tar");
+ extractGzArchive(new FileInputStream(sourceDistribution), uncompressedFile);
+ extract(new TarArchiveInputStream(new FileInputStream(uncompressedFile)), _targetFolder);
+ FileUtils.forceDelete(uncompressedFile);
+ }
+
+ private static void extractZipDistribution(File sourceDistribution, File _targetFolder)
+ throws IOException {
+ extract(new ZipArchiveInputStream(new FileInputStream(sourceDistribution)), _targetFolder);
+ }
+
+ private static void extractGzArchive(InputStream tarGz, File tar) throws IOException {
+ BufferedInputStream in = new BufferedInputStream(tarGz);
+ FileOutputStream out = new FileOutputStream(tar);
+ GzipCompressorInputStream gzIn = new GzipCompressorInputStream(in);
+ final byte[] buffer = new byte[1000];
+ int n = 0;
+ while (-1 != (n = gzIn.read(buffer))) {
+ out.write(buffer, 0, n);
+ }
+ out.close();
+ gzIn.close();
+ }
+
+ private static void extract(ArchiveInputStream is, File targetDir) throws IOException {
+ try {
+ if (targetDir.exists()) {
+ FileUtils.forceDelete(targetDir);
+ }
+ targetDir.mkdirs();
+ ArchiveEntry entry = is.getNextEntry();
+ while (entry != null) {
+ String name = entry.getName();
+ name = name.substring(name.indexOf("/") + 1);
+ File file = new File(targetDir, name);
+ if (entry.isDirectory()) {
+ file.mkdirs();
+ }
+ else {
+ file.getParentFile().mkdirs();
+ OutputStream os = new FileOutputStream(file);
+ try {
+ IOUtils.copy(is, os);
+ }
+ finally {
+ IOUtils.closeQuietly(os);
+ }
+ }
+ entry = is.getNextEntry();
+ }
+ }
+ finally {
+ is.close();
+ }
+ }
+
+ protected static boolean isMavenUrl(String name) {
+ Matcher m = mvnPattern.matcher(name);
+ return m.matches();
+ }
+
+ private File resolveFile(String file) {
+ File fileResolved = null;
+
+ if (isMavenUrl(file)) {
+ fileResolved = new File(fromMaven(file));
+ try {
+ Artifact artifactTemp = resourceToArtifact(file, false);
+ if (!fileResolved.exists()) {
+ try {
+ artifactResolver.resolve(artifactTemp, remoteRepos, localRepo);
+ fileResolved = artifactTemp.getFile();
+ } catch (ArtifactResolutionException e) {
+ getLog().error("Artifact was not resolved", e);
+ } catch (ArtifactNotFoundException e) {
+ getLog().error("Artifact was not found", e);
+ }
+ }
+ } catch (MojoExecutionException e) {
+ getLog().error(e);
+ }
+ } else {
+ fileResolved = new File(file);
+ }
+
+ return fileResolved;
+ }
+
+ /**
+ * Return a path for an artifact:
+ * - if the input is already a path (doesn't contain ':'), the same path is returned.
+ * - if the input is a Maven URL, the input is converted to a default repository location path, type and classifier
+ * are optional.
+ *
+ * @param name artifact data
+ * @return path as supplied or a default Maven repository path
+ */
+ private static String fromMaven(String name) {
+ Matcher m = mvnPattern.matcher(name);
+ if (!m.matches()) {
+ return name;
+ }
+
+ StringBuilder b = new StringBuilder();
+ b.append(m.group(1));
+ for (int i = 0; i < b.length(); i++) {
+ if (b.charAt(i) == '.') {
+ b.setCharAt(i, '/');
+ }
+ }
+ b.append("/"); // groupId
+ String artifactId = m.group(2);
+ String version = m.group(3);
+ String extension = m.group(5);
+ String classifier = m.group(7);
+ b.append(artifactId).append("/"); // artifactId
+ b.append(version).append("/"); // version
+ b.append(artifactId).append("-").append(version);
+ if (present(classifier)) {
+ b.append("-").append(classifier);
+ }
+ if (present(classifier)) {
+ b.append(".").append(extension);
+ } else {
+ b.append(".jar");
+ }
+ return b.toString();
+ }
+
+ private static boolean present(String part) {
+ return part != null && !part.isEmpty();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/karaf/blob/335620ad/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/ClientMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/ClientMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/ClientMojo.java
new file mode 100644
index 0000000..ae602c0
--- /dev/null
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/ClientMojo.java
@@ -0,0 +1,257 @@
+/**
+ *
+ * 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.karaf.tooling.client;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+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.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.local.AgentImpl;
+import org.apache.sshd.agent.local.LocalAgentFactory;
+import org.apache.sshd.client.UserInteraction;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+
+import org.fusesource.jansi.Ansi;
+import org.fusesource.jansi.Ansi.Color;
+import org.fusesource.jansi.AnsiConsole;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Console;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.security.KeyPair;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Client MOJO to deployWithSsh command on a running Karaf instance
+ */
+@Mojo(name = "client", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME)
+public class ClientMojo extends AbstractMojo {
+
+ @Parameter(defaultValue = "8101")
+ private int port;
+
+ @Parameter(defaultValue = "localhost")
+ private String host;
+
+ @Parameter(defaultValue = "karaf")
+ private String user;
+
+ @Parameter(defaultValue = "karaf")
+ private String password;
+
+ @Parameter(defaultValue = "0")
+ private int attempts;
+
+ @Parameter(defaultValue = "2")
+ private int delay;
+
+ @Parameter
+ private List<String> commands;
+
+ @Parameter
+ private List<File> scripts;
+
+ @Parameter
+ private File keyFile;
+
+ private static final String NEW_LINE = System.getProperty("line.separator");
+
+ public void execute() throws MojoExecutionException {
+ // Add commands from scripts to already declared commands
+ for (File script : scripts) {
+ try (BufferedReader br = new BufferedReader(new FileReader(script))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ line = line.trim();
+ if (line.isEmpty()) {
+ continue;
+ }
+ commands.add(line);
+ }
+ }
+ catch (Exception e) {
+ throw new MojoExecutionException(e, e.getMessage(), e.toString());
+ }
+ }
+
+ if (commands.isEmpty()) {
+ getLog().warn("No OSGi command was specified");
+ return;
+ }
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ for (String cmd : commands) {
+ getLog().info(cmd);
+ pw.println(cmd);
+ }
+ execute(sw.toString());
+ }
+
+ protected void execute(String cmd) throws MojoExecutionException {
+ SshClient client = null;
+ try {
+ final Console console = System.console();
+ client = SshClient.setUpDefaultClient();
+ setupAgent(user, keyFile, client);
+
+ client.setUserInteraction( new UserInteraction() {
+ public void welcome(String banner) {
+ console.printf(banner);
+ }
+
+ public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo)
+ {
+ String[] answers = new String[prompt.length];
+ try {
+ for (int i = 0; i < prompt.length; i++) {
+ if (console != null) {
+ if (echo[i]) {
+ answers[i] = console.readLine(prompt[i] + " ");
+ }
+ else {
+ answers[i] = new String( console.readPassword(prompt[i] + " "));
+ }
+ }
+ }
+ }
+ catch (IOError e) {
+ }
+ return answers;
+ }
+ });
+ client.start();
+ if (console != null) {
+ console.printf("Logging in as %s\n", user);
+ }
+ ClientSession session = connect(client);
+ if (password != null) {
+ session.addPasswordIdentity(password);
+ }
+ session.auth().verify();
+
+ final ClientChannel channel = session.createChannel("exec", cmd.concat(NEW_LINE));
+ channel.setIn(new ByteArrayInputStream(new byte[0]));
+ final ByteArrayOutputStream sout = new ByteArrayOutputStream();
+ final ByteArrayOutputStream serr = new ByteArrayOutputStream();
+ channel.setOut( AnsiConsole.wrapOutputStream(sout));
+ channel.setErr( AnsiConsole.wrapOutputStream(serr));
+ channel.open();
+ channel.waitFor(ClientChannel.CLOSED, 0);
+
+ sout.writeTo(System.out);
+ serr.writeTo(System.err);
+
+ // Expects issue KARAF-2623 is fixed
+ final boolean isError = (channel.getExitStatus() != null && channel.getExitStatus().intValue() != 0);
+ if (isError) {
+ final String errorMarker = Ansi.ansi().fg(Color.RED).toString();
+ final int fromIndex = sout.toString().indexOf(errorMarker) + errorMarker.length();
+ final int toIndex = sout.toString().lastIndexOf(Ansi.ansi().fg(Color.DEFAULT ).toString());
+ throw new MojoExecutionException(NEW_LINE + sout.toString().substring(fromIndex, toIndex));
+ }
+ }
+ catch (MojoExecutionException e) {
+ throw e;
+ }
+ catch (Throwable t) {
+ throw new MojoExecutionException(t, t.getMessage(), t.toString());
+ }
+ finally {
+ try {
+ client.stop();
+ }
+ catch (Throwable t) {
+ throw new MojoExecutionException(t, t.getMessage(), t.toString());
+ }
+ }
+ }
+
+ private void setupAgent(String user, File keyFile, SshClient client) {
+ URL builtInPrivateKey = ClientMojo.class.getClassLoader().getResource("karaf.key");
+ SshAgent agent = startAgent(user, builtInPrivateKey, keyFile);
+ client.setAgentFactory( new LocalAgentFactory(agent));
+ client.getProperties().put(SshAgent.SSH_AUTHSOCKET_ENV_NAME, "local");
+ }
+
+ private SshAgent startAgent(String user, URL privateKeyUrl, File keyFile) {
+ try (InputStream is = privateKeyUrl.openStream())
+ {
+ SshAgent agent = new AgentImpl();
+ ObjectInputStream r = new ObjectInputStream(is);
+ KeyPair keyPair = (KeyPair) r.readObject();
+ is.close();
+ agent.addIdentity(keyPair, user);
+ if (keyFile != null) {
+ String[] keyFiles = new String[] { keyFile.getAbsolutePath() };
+ FileKeyPairProvider fileKeyPairProvider = new FileKeyPairProvider(keyFiles);
+ for (KeyPair key : fileKeyPairProvider.loadKeys()) {
+ agent.addIdentity(key, user);
+ }
+ }
+ return agent;
+ }
+ catch (Throwable e) {
+ getLog().error("Error starting ssh agent for: " + e.getMessage(), e);
+ return null;
+ }
+ }
+
+ private ClientSession connect(SshClient client) throws IOException, InterruptedException {
+ int retries = 0;
+ ClientSession session = null;
+ do {
+ final ConnectFuture future = client.connect(user, host, port);
+ future.await();
+ try {
+ session = future.getSession();
+ }
+ catch (RuntimeSshException ex) {
+ if (retries++ < attempts) {
+ Thread.sleep(TimeUnit.SECONDS.toMillis(delay));
+ getLog().info("retrying (attempt " + retries + ") ...");
+ }
+ else {
+ throw ex;
+ }
+ }
+ } while (session == null);
+ return session;
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/karaf/blob/335620ad/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/DeployMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/DeployMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/DeployMojo.java
new file mode 100644
index 0000000..07b516c
--- /dev/null
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/client/DeployMojo.java
@@ -0,0 +1,283 @@
+/**
+ *
+ * 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.karaf.tooling.client;
+
+import org.apache.karaf.tooling.utils.MojoSupport;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+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.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.agent.SshAgent;
+import org.apache.sshd.agent.local.AgentImpl;
+import org.apache.sshd.agent.local.LocalAgentFactory;
+import org.apache.sshd.client.UserInteraction;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.common.RuntimeSshException;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+
+import org.fusesource.jansi.Ansi;
+import org.fusesource.jansi.Ansi.Color;
+import org.fusesource.jansi.AnsiConsole;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.MBeanServerForwarder;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Console;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Deploy MOJO to deploy an artifact remotely on a running Karaf instance, using ssh or JMX
+ */
+@Mojo(name = "deploy", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME)
+public class DeployMojo extends MojoSupport {
+
+ @Parameter(defaultValue = "8101")
+ private int port;
+
+ @Parameter(defaultValue = "localhost")
+ private String host;
+
+ @Parameter(defaultValue = "karaf")
+ private String user;
+
+ @Parameter(defaultValue = "karaf")
+ private String password;
+
+ @Parameter(defaultValue = "karaf-root")
+ private String instance = "karaf-root";
+
+ @Parameter(defaultValue = "0")
+ private int attempts;
+
+ @Parameter(defaultValue = "2")
+ private int delay;
+
+ @Parameter(defaultValue = "true")
+ private boolean useSsh = true;
+
+ @Parameter(defaultValue = "true")
+ private boolean useProjectArtifact = true;
+
+ @Parameter
+ List<String> artifactLocations;
+
+ @Parameter
+ private File keyFile;
+
+ private static final String NEW_LINE = System.getProperty("line.separator");
+
+ public void execute() throws MojoExecutionException {
+ List<String> artifacts = new ArrayList<>();
+ if (useProjectArtifact) {
+ Artifact projectArtifact = project.getArtifact();
+ artifacts.add("mvn:" + projectArtifact.getGroupId() + "/" + projectArtifact.getArtifactId() + "/" + projectArtifact.getVersion());
+ }
+ artifacts.addAll(artifactLocations);
+ if (useSsh)
+ deployWithSsh(artifactLocations);
+ else deployWithJmx(artifactLocations);
+ }
+
+ protected void deployWithJmx(List<String> locations) throws MojoExecutionException {
+ try {
+ JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/" + instance);
+ ArrayList<String> list = new ArrayList<>();
+ if (user != null)
+ list.add(user);
+ if (password != null)
+ list.add(password);
+ HashMap env = new HashMap();
+ String[] credentials = list.toArray(new String[list.size()]);
+ env.put(JMXConnector.CREDENTIALS, credentials);
+ JMXConnector jmxConnector = null;
+ if (credentials.length > 0)
+ jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, env);
+ else jmxConnector = JMXConnectorFactory.connect(jmxServiceURL);
+ MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
+ for (String location : locations) {
+ mBeanServerConnection.invoke(new ObjectName("org.apache.karaf:type=bundle,name=*"), "install", new Object[]{ location, true }, new String[]{ "java.lang.String", "boolean" });
+ }
+ } catch (Exception e) {
+ throw new MojoExecutionException("Can't deploy using JMX", e);
+ }
+ }
+
+ protected void deployWithSsh(List<String> locations) throws MojoExecutionException {
+ SshClient client = null;
+ try {
+ final Console console = System.console();
+ client = SshClient.setUpDefaultClient();
+ setupAgent(user, keyFile, client);
+
+ client.setUserInteraction( new UserInteraction() {
+ public void welcome(String banner) {
+ console.printf(banner);
+ }
+
+ public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo)
+ {
+ String[] answers = new String[prompt.length];
+ try {
+ for (int i = 0; i < prompt.length; i++) {
+ if (console != null) {
+ if (echo[i]) {
+ answers[i] = console.readLine(prompt[i] + " ");
+ }
+ else {
+ answers[i] = new String( console.readPassword(prompt[i] + " "));
+ }
+ }
+ }
+ }
+ catch (IOError e) {
+ }
+ return answers;
+ }
+ });
+ client.start();
+ if (console != null) {
+ console.printf("Logging in as %s\n", user);
+ }
+ ClientSession session = connect(client);
+ if (password != null) {
+ session.addPasswordIdentity(password);
+ }
+ session.auth().verify();
+
+ StringWriter writer = new StringWriter();
+ PrintWriter print = new PrintWriter(writer, true);
+ for (String location : locations) {
+ print.println("bundle:install -s " + location);
+ }
+
+ final ClientChannel channel = session.createChannel("exec", print.toString().concat(NEW_LINE));
+ channel.setIn(new ByteArrayInputStream(new byte[0]));
+ final ByteArrayOutputStream sout = new ByteArrayOutputStream();
+ final ByteArrayOutputStream serr = new ByteArrayOutputStream();
+ channel.setOut( AnsiConsole.wrapOutputStream(sout));
+ channel.setErr( AnsiConsole.wrapOutputStream(serr));
+ channel.open();
+ channel.waitFor(ClientChannel.CLOSED, 0);
+
+ sout.writeTo(System.out);
+ serr.writeTo(System.err);
+
+ // Expects issue KARAF-2623 is fixed
+ final boolean isError = (channel.getExitStatus() != null && channel.getExitStatus().intValue() != 0);
+ if (isError) {
+ final String errorMarker = Ansi.ansi().fg(Color.RED).toString();
+ final int fromIndex = sout.toString().indexOf(errorMarker) + errorMarker.length();
+ final int toIndex = sout.toString().lastIndexOf(Ansi.ansi().fg(Color.DEFAULT ).toString());
+ throw new MojoExecutionException(NEW_LINE + sout.toString().substring(fromIndex, toIndex));
+ }
+ }
+ catch (MojoExecutionException e) {
+ throw e;
+ }
+ catch (Throwable t) {
+ throw new MojoExecutionException(t, t.getMessage(), t.toString());
+ }
+ finally {
+ try {
+ client.stop();
+ }
+ catch (Throwable t) {
+ throw new MojoExecutionException(t, t.getMessage(), t.toString());
+ }
+ }
+ }
+
+ private void setupAgent(String user, File keyFile, SshClient client) {
+ URL builtInPrivateKey = ClientMojo.class.getClassLoader().getResource("karaf.key");
+ SshAgent agent = startAgent(user, builtInPrivateKey, keyFile);
+ client.setAgentFactory( new LocalAgentFactory(agent));
+ client.getProperties().put(SshAgent.SSH_AUTHSOCKET_ENV_NAME, "local");
+ }
+
+ private SshAgent startAgent(String user, URL privateKeyUrl, File keyFile) {
+ try (InputStream is = privateKeyUrl.openStream())
+ {
+ SshAgent agent = new AgentImpl();
+ ObjectInputStream r = new ObjectInputStream(is);
+ KeyPair keyPair = (KeyPair) r.readObject();
+ is.close();
+ agent.addIdentity(keyPair, user);
+ if (keyFile != null) {
+ String[] keyFiles = new String[] { keyFile.getAbsolutePath() };
+ FileKeyPairProvider fileKeyPairProvider = new FileKeyPairProvider(keyFiles);
+ for (KeyPair key : fileKeyPairProvider.loadKeys()) {
+ agent.addIdentity(key, user);
+ }
+ }
+ return agent;
+ }
+ catch (Throwable e) {
+ getLog().error("Error starting ssh agent for: " + e.getMessage(), e);
+ return null;
+ }
+ }
+
+ private ClientSession connect(SshClient client) throws IOException, InterruptedException {
+ int retries = 0;
+ ClientSession session = null;
+ do {
+ final ConnectFuture future = client.connect(user, host, port);
+ future.await();
+ try {
+ session = future.getSession();
+ }
+ catch (RuntimeSshException ex) {
+ if (retries++ < attempts) {
+ Thread.sleep(TimeUnit.SECONDS.toMillis(delay));
+ getLog().info("retrying (attempt " + retries + ") ...");
+ }
+ else {
+ throw ex;
+ }
+ }
+ } while (session == null);
+ return session;
+ }
+
+}
+