3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org.apache.karaf.tooling.client;
20 import org.apache.karaf.tooling.utils.MojoSupport;
21 import org.apache.maven.artifact.Artifact;
22 import org.apache.maven.plugin.AbstractMojo;
23 import org.apache.maven.plugin.MojoExecutionException;
24 import org.apache.maven.plugins.annotations.LifecyclePhase;
25 import org.apache.maven.plugins.annotations.Mojo;
26 import org.apache.maven.plugins.annotations.Parameter;
27 import org.apache.maven.plugins.annotations.ResolutionScope;
28 import org.apache.sshd.ClientChannel;
29 import org.apache.sshd.ClientSession;
30 import org.apache.sshd.SshClient;
31 import org.apache.sshd.agent.SshAgent;
32 import org.apache.sshd.agent.local.AgentImpl;
33 import org.apache.sshd.agent.local.LocalAgentFactory;
34 import org.apache.sshd.client.UserInteraction;
35 import org.apache.sshd.client.future.ConnectFuture;
36 import org.apache.sshd.common.RuntimeSshException;
37 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
39 import org.fusesource.jansi.Ansi;
40 import org.fusesource.jansi.Ansi.Color;
41 import org.fusesource.jansi.AnsiConsole;
43 import javax.management.MBeanServerConnection;
44 import javax.management.ObjectName;
45 import javax.management.remote.JMXConnector;
46 import javax.management.remote.JMXConnectorFactory;
47 import javax.management.remote.JMXServiceURL;
48 import javax.management.remote.MBeanServerForwarder;
49 import java.io.BufferedReader;
50 import java.io.ByteArrayInputStream;
51 import java.io.ByteArrayOutputStream;
52 import java.io.Console;
54 import java.io.FileReader;
55 import java.io.IOError;
56 import java.io.IOException;
57 import java.io.InputStream;
58 import java.io.ObjectInputStream;
59 import java.io.PrintWriter;
60 import java.io.StringWriter;
62 import java.security.KeyPair;
63 import java.util.ArrayList;
64 import java.util.HashMap;
65 import java.util.List;
66 import java.util.concurrent.TimeUnit;
69 * Deploy MOJO to deploy an artifact remotely on a running Karaf instance, using ssh or JMX
71 @Mojo(name = "deploy", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true)
72 public class DeployMojo extends MojoSupport {
74 @Parameter(defaultValue = "8101")
77 @Parameter(defaultValue = "localhost")
80 @Parameter(defaultValue = "karaf")
83 @Parameter(defaultValue = "karaf")
84 private String password;
86 @Parameter(defaultValue = "karaf-root")
87 private String instance = "karaf-root";
89 @Parameter(defaultValue = "0")
92 @Parameter(defaultValue = "2")
95 @Parameter(defaultValue = "true")
96 private boolean useSsh = true;
98 @Parameter(defaultValue = "true")
99 private boolean useProjectArtifact = true;
102 List<String> artifactLocations;
105 private File keyFile;
107 private static final String NEW_LINE = System.getProperty("line.separator");
109 public void execute() throws MojoExecutionException {
110 List<String> artifacts = new ArrayList<>();
111 if (useProjectArtifact) {
112 Artifact projectArtifact = project.getArtifact();
113 artifacts.add("mvn:" + projectArtifact.getGroupId() + "/" + projectArtifact.getArtifactId() + "/" + projectArtifact.getVersion());
115 artifacts.addAll(artifactLocations);
117 deployWithSsh(artifactLocations);
118 else deployWithJmx(artifactLocations);
121 protected void deployWithJmx(List<String> locations) throws MojoExecutionException {
123 JMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/" + instance);
124 ArrayList<String> list = new ArrayList<>();
127 if (password != null)
129 HashMap env = new HashMap();
130 String[] credentials = list.toArray(new String[list.size()]);
131 env.put(JMXConnector.CREDENTIALS, credentials);
132 JMXConnector jmxConnector = null;
133 if (credentials.length > 0)
134 jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, env);
135 else jmxConnector = JMXConnectorFactory.connect(jmxServiceURL);
136 MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
137 for (String location : locations) {
138 mBeanServerConnection.invoke(new ObjectName("org.apache.karaf:type=bundle,name=*"), "install", new Object[]{ location, true }, new String[]{ "java.lang.String", "boolean" });
140 } catch (Exception e) {
141 throw new MojoExecutionException("Can't deploy using JMX", e);
145 protected void deployWithSsh(List<String> locations) throws MojoExecutionException {
146 SshClient client = null;
148 final Console console = System.console();
149 client = SshClient.setUpDefaultClient();
150 setupAgent(user, keyFile, client);
152 client.setUserInteraction( new UserInteraction() {
153 public void welcome(String banner) {
154 console.printf(banner);
157 public String[] interactive(String destination, String name, String instruction, String[] prompt, boolean[] echo)
159 String[] answers = new String[prompt.length];
161 for (int i = 0; i < prompt.length; i++) {
162 if (console != null) {
164 answers[i] = console.readLine(prompt[i] + " ");
167 answers[i] = new String( console.readPassword(prompt[i] + " "));
178 if (console != null) {
179 console.printf("Logging in as %s\n", user);
181 ClientSession session = connect(client);
182 if (password != null) {
183 session.addPasswordIdentity(password);
185 session.auth().verify();
187 StringWriter writer = new StringWriter();
188 PrintWriter print = new PrintWriter(writer, true);
189 for (String location : locations) {
190 print.println("bundle:install -s " + location);
193 final ClientChannel channel = session.createChannel("exec", print.toString().concat(NEW_LINE));
194 channel.setIn(new ByteArrayInputStream(new byte[0]));
195 final ByteArrayOutputStream sout = new ByteArrayOutputStream();
196 final ByteArrayOutputStream serr = new ByteArrayOutputStream();
197 channel.setOut( AnsiConsole.wrapOutputStream(sout));
198 channel.setErr( AnsiConsole.wrapOutputStream(serr));
200 channel.waitFor(ClientChannel.CLOSED, 0);
202 sout.writeTo(System.out);
203 serr.writeTo(System.err);
205 // Expects issue KARAF-2623 is fixed
206 final boolean isError = (channel.getExitStatus() != null && channel.getExitStatus().intValue() != 0);
208 final String errorMarker = Ansi.ansi().fg(Color.RED).toString();
209 final int fromIndex = sout.toString().indexOf(errorMarker) + errorMarker.length();
210 final int toIndex = sout.toString().lastIndexOf(Ansi.ansi().fg(Color.DEFAULT ).toString());
211 throw new MojoExecutionException(NEW_LINE + sout.toString().substring(fromIndex, toIndex));
214 catch (MojoExecutionException e) {
217 catch (Throwable t) {
218 throw new MojoExecutionException(t, t.getMessage(), t.toString());
224 catch (Throwable t) {
225 throw new MojoExecutionException(t, t.getMessage(), t.toString());
230 private void setupAgent(String user, File keyFile, SshClient client) {
231 URL builtInPrivateKey = ClientMojo.class.getClassLoader().getResource("karaf.key");
232 SshAgent agent = startAgent(user, builtInPrivateKey, keyFile);
233 client.setAgentFactory( new LocalAgentFactory(agent));
234 client.getProperties().put(SshAgent.SSH_AUTHSOCKET_ENV_NAME, "local");
237 private SshAgent startAgent(String user, URL privateKeyUrl, File keyFile) {
238 try (InputStream is = privateKeyUrl.openStream())
240 SshAgent agent = new AgentImpl();
241 ObjectInputStream r = new ObjectInputStream(is);
242 KeyPair keyPair = (KeyPair) r.readObject();
244 agent.addIdentity(keyPair, user);
245 if (keyFile != null) {
246 String[] keyFiles = new String[] { keyFile.getAbsolutePath() };
247 FileKeyPairProvider fileKeyPairProvider = new FileKeyPairProvider(keyFiles);
248 for (KeyPair key : fileKeyPairProvider.loadKeys()) {
249 agent.addIdentity(key, user);
254 catch (Throwable e) {
255 getLog().error("Error starting ssh agent for: " + e.getMessage(), e);
260 private ClientSession connect(SshClient client) throws IOException, InterruptedException {
262 ClientSession session = null;
264 final ConnectFuture future = client.connect(user, host, port);
267 session = future.getSession();
269 catch (RuntimeSshException ex) {
270 if (retries++ < attempts) {
271 Thread.sleep(TimeUnit.SECONDS.toMillis(delay));
272 getLog().info("retrying (attempt " + retries + ") ...");
278 } while (session == null);