# HG changeset patch # User Andrew Krasny # Date 1339007056 -14400 # Node ID b7462934d0f478ec56ba94a2479a83fd901766e2 # Parent a308c29718f4533fc177b6e098812e969276f92b [mq]: EXEC_API diff --git a/libs.jna/nbproject/project.xml b/libs.jna/nbproject/project.xml --- a/libs.jna/nbproject/project.xml +++ b/libs.jna/nbproject/project.xml @@ -71,15 +71,17 @@ org.jruby org.netbeans.core.browser.xulrunner org.netbeans.core.nativeaccess + org.netbeans.libs.svnClientAdapter.svnkit org.netbeans.modules.dlight.nativeexecution org.netbeans.modules.extexecution.destroy + org.netbeans.modules.ide.execution org.netbeans.modules.keyring.impl + org.netbeans.modules.masterfs.linux + org.netbeans.modules.masterfs.macosx + org.netbeans.modules.masterfs.solaris org.netbeans.modules.masterfs.windows - org.netbeans.modules.masterfs.linux - org.netbeans.modules.masterfs.solaris - org.netbeans.modules.masterfs.macosx + org.netbeans.modules.nativeexecution.impl org.netbeans.modules.python.qshell - org.netbeans.libs.svnClientAdapter.svnkit com.sun.jna com.sun.jna.ptr com.sun.jna.win32 diff --git a/nativeexecution.impl/build.xml b/nativeexecution.impl/build.xml new file mode 100755 --- /dev/null +++ b/nativeexecution.impl/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.nativeexecution.impl + + diff --git a/nativeexecution.impl/manifest.mf b/nativeexecution.impl/manifest.mf new file mode 100755 --- /dev/null +++ b/nativeexecution.impl/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: false +OpenIDE-Module: org.netbeans.modules.nativeexecution.impl +OpenIDE-Module-Implementation-Version: 1 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/nativeexecution/impl/resources/Bundle.properties +OpenIDE-Module-Provides: ConnectionService.localhost, ConnectionService.ssh diff --git a/nativeexecution.impl/nbproject/project.properties b/nativeexecution.impl/nbproject/project.properties new file mode 100755 --- /dev/null +++ b/nativeexecution.impl/nbproject/project.properties @@ -0,0 +1,5 @@ +is.autoload=true +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial +spec.version.base=1.0 + diff --git a/nativeexecution.impl/nbproject/project.xml b/nativeexecution.impl/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/nbproject/project.xml @@ -0,0 +1,87 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.nativeexecution.impl + + + com.jcraft.jsch + + + + 0.1.43 + + + + org.netbeans.api.progress + + + + 1 + 1.28 + + + + org.netbeans.libs.jna + + + + 1 + 1.4 + + + + org.netbeans.modules.nativeexecution + + + + 1.0 + + + + org.openide.dialogs + + + + 7.25 + + + + org.openide.util + + + + 8.24 + + + + org.openide.util.lookup + + + + 8.15 + + + + org.openide.windows + + + + 6.55 + + + + + + unit + + org.netbeans.libs.junit4 + + + + + + + + diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/ExecutionLogger.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/ExecutionLogger.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/ExecutionLogger.java @@ -0,0 +1,188 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl; + +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.StreamHandler; +import javax.swing.SwingUtilities; + +/** + * + * @author akrasny + */ +public final class ExecutionLogger extends Logger { + + private static final boolean assertionsEnabled; + private static final ExecutionLogger instance = new ExecutionLogger(); + private static final Map map = new WeakHashMap(); + + static { + boolean ea = false; + assert (ea = true); + assertionsEnabled = ea; + String level_str = System.getProperty("ide.execution.logger.level", "INFO").toUpperCase(); // NOI18N + Level level = Level.INFO; + + try { + level = Level.parse(level_str); + } catch (IllegalArgumentException ex) { + } + + instance.addHandler(new LoggerHandler()); + instance.setLevel(level); + } + private static final long startTimeMillis = System.currentTimeMillis(); + + private static class LoggerHandler extends StreamHandler { + + public LoggerHandler() { + setOutputStream(System.out); + } + + @Override + public void publish(LogRecord record) { + record.setMessage("[" + (record.getMillis() - startTimeMillis) + " ms.] " + record.getMessage()); // NOI18N + super.publish(record); + super.flush(); + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } + } + + private ExecutionLogger() { + super("nativeexecution.impl.logger", null); // NOI18N + setLevel(Level.ALL); + } + + public static ExecutionLogger getInstance() { + return instance; + } + + public Ref reportStart(String descr) { + Ref r = new Ref(); + log(Level.INFO, "{0} STARTED", descr); // NOI18N + synchronized (map) { + map.put(r, descr); + } + return r; + } + + public void reportDone(Ref ref) { + String descr; + synchronized (map) { + descr = map.remove(ref); + } + if (descr == null) { + return; + } + log(Level.INFO, "{0} FINISHED in {1} ms.", new Object[]{descr, System.currentTimeMillis() - ref.ts}); // NOI18N + } + + public static void assertTrue(boolean value) { + if (assertionsEnabled && !value) { + String message = "Assertion error"; // NOI18N + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertTrue(boolean value, String message) { + if (assertionsEnabled && !value) { + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertFalse(boolean value) { + if (assertionsEnabled && value) { + String message = "Assertion error"; // NOI18N + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertFalse(boolean value, String message) { + if (assertionsEnabled && value) { + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertNonUiThread(String message) { + if (assertionsEnabled && SwingUtilities.isEventDispatchThread()) { + instance.log(Level.FINE, message, new Exception(message)); + } + } + + public static void assertNonUiThread() { + assertNonUiThread("Should not be called from UI thread"); // NOI18N + } + + public static void fullThreadDump(String title) { + final Set> stack = Thread.getAllStackTraces().entrySet(); + System.err.printf("----- %s Start Thread Dump-----\n", title == null ? "" : title); // NOI18N + for (Map.Entry entry : stack) { + System.err.println(entry.getKey().getName()); + for (StackTraceElement element : entry.getValue()) { + System.err.println("\tat " + element.toString()); // NOI18N + } + System.err.println(); + } + System.err.println("----- End Thread Dump-----"); // NOI18N + } + + public static class Ref { + + private final long ts; + + private Ref() { + this.ts = System.currentTimeMillis(); + } + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/AbstractNativeProcessImpl.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/AbstractNativeProcessImpl.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/AbstractNativeProcessImpl.java @@ -0,0 +1,202 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.common; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; +import org.openide.util.Utilities; + +/** + * + * @author akrasny + */ +public abstract class AbstractNativeProcessImpl implements NativeProcessImplementation { + + private final NativeProcessParams params; + private InputStream inputStream; + private InputStream errorStream; + private OutputStream outputStream; + private int pid; + + public AbstractNativeProcessImpl(NativeProcessParams params) { + this.params = params; + } + + public final NativeProcessImplementation createAndStart() throws NativeExecutionException { + ProcessStreams streams; + + try { + String shellScript = params.getShellScript(); + + if (shellScript != null) { + streams = create(params.getShell(), params.getShellScript()); + } else { + streams = create(params.getCommand()); + } + + inputStream = streams.is; + errorStream = streams.es; + outputStream = streams.os; + pid = streams.pid; + } catch (Throwable ex) { + throw new NativeExecutionException(ex); + } + + return this; + } + + protected final NativeProcessParams getParams() { + return params; + } + + abstract protected ProcessStreams create(List command) throws NativeExecutionException; + + abstract protected ProcessStreams create(String shell, String script) throws NativeExecutionException; + + @Override + public int getPID() { + return pid; + } + + @Override + public OutputStream getOutputStream() { + return outputStream; + } + + @Override + public InputStream getInputStream() { + return inputStream; + } + + @Override + public InputStream getErrorStream() { + return errorStream; + } + + protected String commandToCommandLine(final List cmd) { + /** + * See IZ#168186 - Wrongly interpreted "$" symbol in arguments + * + * The magic below is all about making run/debug act identically in case + * of ExternalTerminal + */ + StringBuilder sb = new StringBuilder(); + + String exec = cmd.get(0); + + sb.append(quoteSpecialChars(exec)).append(' '); + + String[] sarg = new String[1]; + + boolean escape; + + for (int i = 1; i < cmd.size(); i++) { + String arg = cmd.get(i); + escape = false; + sarg[0] = arg; + arg = Utilities.escapeParameters(sarg); + + sb.append('"'); + + if ((arg.startsWith("'") && arg.endsWith("'")) || // NOI18N + (arg.startsWith("\"") && arg.endsWith("\""))) { // NOI18N + arg = arg.substring(1, arg.length() - 1); + escape = true; + } + + if (escape) { + char pc = 'x'; + + for (char c : arg.toCharArray()) { + if (c == '$' && pc != '\\') { + sb.append('\\'); + } + sb.append(c); + pc = c; + } + } else { + sb.append(arg); + } + + sb.append("\" "); // NOI18N + } + + if (params.isRedirectError()) { + sb.append(" 2>&1"); // NOI18N + } + + return sb.toString().trim(); + } + + private String quoteSpecialChars(String orig) { + StringBuilder sb = new StringBuilder(); + String escapeChars = " &\"'()!"; // NOI18N + + for (char c : orig.toCharArray()) { + if (escapeChars.indexOf(c) >= 0) { // NOI18N + sb.append('\\'); + } + sb.append(c); + } + + return sb.toString(); + } + + protected static final class ProcessStreams { + + private final InputStream is; + private final InputStream es; + private final OutputStream os; + private final int pid; + + public ProcessStreams(InputStream is, InputStream es, OutputStream os, int pid) { + this.is = is; + this.es = es; + this.os = os; + this.pid = pid; + } + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/ShellBasedConnectionInfoProvider.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/ShellBasedConnectionInfoProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/ShellBasedConnectionInfoProvider.java @@ -0,0 +1,195 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.common; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfo; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfoProvider; +import org.netbeans.modules.nativeexecution.util.ProcessInputProvider; +import org.netbeans.modules.nativeexecution.util.ProcessOutputProcessor; +import org.netbeans.modules.nativeexecution.util.ProcessUtils; +import org.openide.util.Exceptions; + +/** + * + * @author Andrew + */ +public final class ShellBasedConnectionInfoProvider implements ConnectionInfoProvider { + + private static final ShellBasedConnectionInfoProvider instance = new ShellBasedConnectionInfoProvider(); + + public static ShellBasedConnectionInfoProvider getInstance() { + return instance; + } + + private ShellBasedConnectionInfoProvider() { + } + + @Override + public ConnectionInfo getConnectionInfo(Connection connection) throws NativeExecutionException { + NativeProcessBuilder npb = connection.newProcessBuilder(); + npb.setShellScript("/bin/sh", "/bin/sh -s"); // NOI18N + final Map map = new HashMap(); + final EnvironmentMap env = EnvironmentMap.createCaseSensitive(':'); + ProcessUtils.execute(npb, new ProcessOutputProcessor() { + + FetcherState state = FetcherState.NONE; + + @Override + /** + * This method relies on the contract: - each property is a SINGLE + * line in format NAME=VALUE - '=' MUST be present on each line + */ + public void processLine(CharSequence line) { + int idx = 0; + int length = line.length(); + + if (length == 0) { + return; + } + + if ("@@@@@INFO".equals(line)) { // NOI18N + state = FetcherState.INFO; + return; + } + + if ("@@@@@ENV".equals(line)) { // NOI18N + state = FetcherState.ENV; + return; + } + + if (state == FetcherState.NONE) { + return; + } + + while (idx < length && line.charAt(idx) != '=') { + idx++; + } + + if (idx >= length) { + return; +// throw new InternalError("Bad line: '" + line + "'"); // NOI18N + } + + String name = line.subSequence(0, idx).toString(); + String value = line.subSequence(idx + 1, length).toString(); + + switch (state) { + case ENV: + env.put(name, value); + break; + case INFO: + map.put(name, value); + break; + } + } + }, null, new ProcessInputProvider() { + + @Override + public void start(BufferedWriter bw) { + try { + bw.append("NB_KEY=").append(getNBKey()).append('\n'); // NOI18N + InputStream in = getClass().getClassLoader().getResourceAsStream("org/netbeans/modules/nativeexecution/impl/resources/hostinfo.sh"); // NOI18N + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String line; + while ((line = br.readLine()) != null) { + bw.append(line).append('\n'); + } + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } finally { + try { + bw.close(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + } + }); + + return new ConnectionInfo() { + + @Override + public EnvironmentMap getEnvironmentMap() { + return env; + } + + @Override + public Map getProperties() { + return map; + } + }; + } + + /** + * @return unique key of the current NB instance, introduced to fix bug + * #176526 + */ + private String getNBKey() { + // use NB userdir to prevent local collisions + int hashCode = System.getProperty("netbeans.user", "").hashCode(); + try { + // use host name to prevent remote collisions + InetAddress localhost = InetAddress.getLocalHost(); + hashCode = 3 * hashCode + 5 * localhost.getHostName().hashCode(); + } catch (UnknownHostException ex) { + } + return Integer.toHexString(hashCode); + } + + private enum FetcherState { + + INFO, ENV, NONE + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/Bundle.properties b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/Bundle.properties new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/Bundle.properties @@ -0,0 +1,42 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2012 Oracle and/or its affiliates. All rights reserved. +# +# Oracle and Java are registered trademarks of Oracle and/or its affiliates. +# Other names may be trademarks of their respective owners. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. +# +# Contributor(s): +# +# Portions Copyrighted 2012 Sun Microsystems, Inc. + +TITLE_YN_Warning=Warning +MSG_PasswordInteractive={0} {1} diff --git a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/jsch/JSchChannelsSupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchChannelsSupport.java copy from dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/jsch/JSchChannelsSupport.java copy to nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchChannelsSupport.java --- a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/jsch/JSchChannelsSupport.java +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchChannelsSupport.java @@ -36,14 +36,14 @@ * * Portions Copyrighted 2010 Sun Microsystems, Inc. */ -package org.netbeans.modules.nativeexecution.jsch; +package org.netbeans.modules.nativeexecution.impl.jsch; import com.jcraft.jsch.Channel; -import com.jcraft.jsch.ChannelShell; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; -import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -51,16 +51,15 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.util.PasswordManager; -import org.netbeans.modules.nativeexecution.support.Logger; -import org.netbeans.modules.nativeexecution.support.RemoteUserInfo; -import org.openide.util.Cancellable; +import org.netbeans.modules.nativeexecution.api.ConnectionCancelledException; +import org.netbeans.modules.nativeexecution.api.ConnectionException; +import org.netbeans.modules.nativeexecution.impl.ExecutionLogger; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.openide.util.Exceptions; /** * @@ -68,21 +67,23 @@ */ public final class JSchChannelsSupport { - private static final java.util.logging.Logger log = Logger.getInstance(); + private static final ExecutionLogger log = ExecutionLogger.getInstance(); private static final int JSCH_CONNECTION_RETRY = Integer.getInteger("jsch.connection.retry", 3); // NOI18N private static final int JSCH_CONNECTION_TIMEOUT = Integer.getInteger("jsch.connection.timeout", 10000); // NOI18N private static final int JSCH_SESSIONS_PER_ENV = Integer.getInteger("jsch.sessions.per.env", 10); // NOI18N private static final int JSCH_CHANNELS_PER_SESSION = Integer.getInteger("jsch.channels.per.session", 10); // NOI18N - private static final boolean UNIT_TEST_MODE = Boolean.getBoolean("nativeexecution.mode.unittest"); // NOI18N private static final boolean USE_JZLIB = Boolean.getBoolean("jzlib"); // NOI18N private static final HashMap jschSessionConfig = new HashMap(); private final JSch jsch; + private final String user; + private final String host; + private final int port; private final RemoteUserInfo userInfo; - private final ExecutionEnvironment env; private final ReentrantLock sessionsLock = new ReentrantLock(); private final Condition sessionAvailable = sessionsLock.newCondition(); // AtomicInteger stores a number of available channels for the session - // We use ConcurrentHashMap to be able fast isConnected() check; in most other cases sessions is guarded bu "this" + // We use ConcurrentHashMap to be able to provide fast isConnected() check; + // in most other cases sessions variable is guarded by "this" private final ConcurrentHashMap sessions = new ConcurrentHashMap(); private final Set knownChannels = new HashSet(); @@ -103,18 +104,48 @@ } } } + private final URI uri; - public JSchChannelsSupport(JSch jsch, ExecutionEnvironment env) { + private JSchChannelsSupport(JSch jsch, URI uri, String user, String host, Integer port, RemoteUserInfo userInfo) { this.jsch = jsch; - this.env = env; - this.userInfo = new RemoteUserInfo(env, !UNIT_TEST_MODE); + this.uri = uri; + this.user = user; + this.host = host; + this.port = port; + this.userInfo = userInfo; } - public ChannelShell getShellChannel(boolean waitIfNoAvailable) throws JSchException, IOException, InterruptedException { - return (ChannelShell) acquireChannel("shell", waitIfNoAvailable); // NOI18N + public static JSchChannelsSupport get(URI uri, AuthDataProvider dataProvider) throws InterruptedException { + String user_ = dataProvider.getProperty(uri, String.class, AuthDataProvider.USERNAME); + String host_ = dataProvider.getProperty(uri, String.class, AuthDataProvider.HOST); + Integer port_ = dataProvider.getProperty(uri, Integer.class, AuthDataProvider.PORT); + RemoteUserInfo userInfo_ = new RemoteUserInfo(uri, dataProvider); + JSch jsch_ = new JSch(); + initJsch(jsch_, uri, dataProvider); + + return new JSchChannelsSupport(jsch_, uri, user_, host_, port_, userInfo_); } - public synchronized Channel acquireChannel(String type, boolean waitIfNoAvailable) throws JSchException, IOException, InterruptedException { + private static void initJsch(JSch jsch, URI uri, AuthDataProvider dataProvider) throws InterruptedException { + try { + jsch.removeAllIdentity(); + String authType = dataProvider.getProperty(uri, String.class, "AuthType"); + String knownHostsFile = dataProvider.getProperty(uri, String.class, "KnownHostsFile"); + String sshKeyFile = dataProvider.getProperty(uri, String.class, "SSHKeyFile"); + + if (knownHostsFile != null) { + jsch.setKnownHosts(knownHostsFile); + } + + if ("SSHKey".equals(authType) && sshKeyFile != null) { + jsch.addIdentity(sshKeyFile); + } + } catch (JSchException ex) { + Exceptions.printStackTrace(ex); + } + } + + public synchronized Channel acquireChannel(String type, boolean waitIfNoAvailable) throws ConnectionException { JSchException exception = null; for (int i = 0; i < JSCH_CONNECTION_RETRY; i++) { @@ -126,14 +157,19 @@ try { sessionsLock.lock(); while (session == null) { - sessionAvailable.await(); + try { + sessionAvailable.await(); + } catch (InterruptedException ex) { + Thread.interrupted(); + throw new ConnectionCancelledException(uri); + } session = findFreeSession(); } } finally { sessionsLock.unlock(); } } else { - throw new IOException("All " + JSCH_SESSIONS_PER_ENV + " sessions for " + env.getDisplayName() + " are fully loaded"); // NOI18N + throw new ConnectionException(uri, "All " + JSCH_SESSIONS_PER_ENV + " sessions for " + host + " are fully loaded"); // NOI18N } } } @@ -164,7 +200,7 @@ // exception is set; or there was another exception => it was thrown // already assert exception != null; - throw exception; + throw new ConnectionException(uri, exception); } public boolean isConnected() { @@ -178,11 +214,10 @@ return false; } - public synchronized void reconnect(ExecutionEnvironment env) throws IOException, JSchException, InterruptedException { - disconnect(); - connect(); - } - +// public synchronized void reconnect(ExecutionEnvironment env) throws ConnectionException { +// disconnect(); +// connect(); +// } private Session findFreeSession() { for (Entry entry : sessions.entrySet()) { Session s = entry.getKey(); @@ -198,7 +233,7 @@ return null; } - public synchronized void connect() throws JSchException, InterruptedException { + public synchronized void connect() throws ConnectionException { if (isConnected()) { return; } @@ -212,56 +247,35 @@ } } - private Session startNewSession(boolean acquireChannel) throws JSchException, InterruptedException { + private Session startNewSession(boolean acquireChannel) throws ConnectionException { Session newSession = null; - final AtomicBoolean cancelled = new AtomicBoolean(false); + try { + newSession = jsch.getSession(user, host, port); + newSession.setUserInfo(userInfo); - ConnectingProgressHandle.startHandle(env, new Cancellable() { - - @Override - public boolean cancel() { - cancelled.set(true); - return true; - } - }); - - try { - while (!cancelled.get()) { - try { - newSession = jsch.getSession(env.getUser(), env.getHostAddress(), env.getSSHPort()); - newSession.setUserInfo(userInfo); - - for (Entry entry : jschSessionConfig.entrySet()) { - newSession.setConfig(entry.getKey(), entry.getValue()); - } - - if (USE_JZLIB) { - newSession.setConfig("compression.s2c", "zlib@openssh.com,zlib,none"); // NOI18N - newSession.setConfig("compression.c2s", "zlib@openssh.com,zlib,none"); // NOI18N - newSession.setConfig("compression_level", "9"); // NOI18N - } - - newSession.connect(JSCH_CONNECTION_TIMEOUT); - break; - } catch (JSchException ex) { - if (!UNIT_TEST_MODE && "Auth fail".equals(ex.getMessage())) { // NOI18N - PasswordManager.getInstance().clearPassword(env); - } else { - throw ex; - } - } + for (Entry entry : jschSessionConfig.entrySet()) { + newSession.setConfig(entry.getKey(), entry.getValue()); } - if (cancelled.get()) { - throw new InterruptedException("StartNewSession was cancelled ..."); // NOI18N + if (USE_JZLIB) { + newSession.setConfig("compression.s2c", "zlib@openssh.com,zlib,none"); // NOI18N + newSession.setConfig("compression.c2s", "zlib@openssh.com,zlib,none"); // NOI18N + newSession.setConfig("compression_level", "9"); // NOI18N } - sessions.put(newSession, new AtomicInteger(JSCH_CHANNELS_PER_SESSION - (acquireChannel ? 1 : 0))); + newSession.connect(JSCH_CONNECTION_TIMEOUT); + } catch (JSchException ex) { + if (ex.getCause() instanceof InterruptedIOException) { + throw new ConnectionCancelledException(uri); + } + throw new ConnectionException(uri, ex); + } finally { + // TODO: notify that password could be eraised + } - log.log(Level.FINE, "New session [{0}] started.", new Object[]{System.identityHashCode(newSession)}); // NOI18N - } finally { - ConnectingProgressHandle.stopHandle(env); - } + sessions.put(newSession, new AtomicInteger(JSCH_CHANNELS_PER_SESSION - (acquireChannel ? 1 : 0))); + + log.log(Level.FINE, "New session [{0}] started.", new Object[]{System.identityHashCode(newSession)}); // NOI18N return newSession; } diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchConnection.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchConnection.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchConnection.java @@ -0,0 +1,126 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.JSchException; +import java.net.URI; +import org.netbeans.modules.nativeexecution.api.BrokenConnectionException; +import org.netbeans.modules.nativeexecution.api.ConnectionException; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.common.ShellBasedConnectionInfoProvider; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author akrasny + */ +public class JSchConnection implements ConnectionImplementation { + + private final JSchChannelsSupport cs; + private final String name; + private final URI uri; + private final Lookup lookup; + + public JSchConnection(URI uri, JSchChannelsSupport cs) { + String user = uri.getUserInfo(); + StringBuilder sb = new StringBuilder(); + sb.append(uri.getScheme()).append("://").append(user.replaceAll(":.*", ":***")).append("@").append(uri.getHost()); // NOI18N + name = sb.toString(); + this.cs = cs; + this.uri = uri; + this.lookup = Lookups.fixed(ShellBasedConnectionInfoProvider.getInstance()); + } + + @Override + public NativeProcessCreator newProcessBuilderImpl() throws NativeExecutionException { + if (!cs.isConnected()) { + throw new BrokenConnectionException(uri); + } + + return new JSchProcessCreator(this); + } + + @Override + public boolean isConnected() { + return cs.isConnected(); + } + + @SuppressWarnings("Unchecked") + ChannelExec getChannelExec(boolean waitIfNoAvailable) throws ConnectionException { + return (ChannelExec) cs.acquireChannel("exec", waitIfNoAvailable); // NOI18N + } + + @SuppressWarnings("Unchecked") + ChannelShell getChannelShell(boolean waitIfNoAvailable) throws ConnectionException { + return (ChannelShell) cs.acquireChannel("shell", waitIfNoAvailable); // NOI18N + } + + @Override + public String toString() { + return name; + } + + void releaseChannel(Channel channel) throws JSchException { + cs.releaseChannel(channel); + } + + @Override + public Lookup getLookup() { + return lookup; + } + + @Override + public void disconnect() { + cs.disconnect(); + } + + @Override + public URI getURI() { + return uri; + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchConnector.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchConnector.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchConnector.java @@ -0,0 +1,157 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.URI; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.netbeans.modules.nativeexecution.api.ConnectionException; +import org.netbeans.modules.nativeexecution.api.DestinationUnreachableException; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.ExecutionLogger; +import org.netbeans.modules.nativeexecution.impl.ExecutionLogger.Ref; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.ConnectorImplementation; +import org.openide.util.RequestProcessor; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.nativeexecution.spi.ConnectorImplementation.class, path = "ConnectionService/ssh", position = 10) +public final class JSchConnector implements ConnectorImplementation { + + private static final ExecutionLogger log = ExecutionLogger.getInstance(); + private static final int SOCKET_CREATION_TIMEOUT = Integer.getInteger("socket.connection.timeout", 10000); // NOI18N + + @Override + public ConnectionImplementation connectTo(final URI uri, final AuthDataProvider authDataProvider) throws InterruptedException, NativeExecutionException { + Ref start = log.reportStart("Connecting to " + uri); + try { + + String host = authDataProvider.getProperty(uri, String.class, AuthDataProvider.HOST); + Integer port = authDataProvider.getProperty(uri, Integer.class, AuthDataProvider.PORT); + + if (!isReachable(host, port)) { + throw new DestinationUnreachableException(uri); + } + + JSchChannelsSupport cs = JSchChannelsSupport.get(uri, authDataProvider); + cs.connect(); + + // OK. Connection established. + return new JSchConnection(uri, cs); + } finally { + log.reportDone(start); + } + } + + private boolean isReachable(final String host, final Integer port) { + // TODO: review! + // This ping-thread-related work makes sense if there are systems + // where interrupting a thread that is in socket.connect() doesn't + // work as expected. Do we know such systems? + + // IZ#165591 - Trying to connect to wrong host breaks remote host setup (for other hosts) + // To prevent this first try to just open a socket and + // go to the jsch code in case of success only. + + // The important thing here is that we still need to be interruptable + // In case of wrong IP address (unreachable) the SocketImpl's connect() + // method may hang in system call for a long period of time, being + // insensitive to interrupts. + // So do this in a separate thread... + + Callable checker = new Callable() { + + @Override + public Boolean call() throws Exception { + final Socket socket = new Socket(); + final SocketAddress addressToConnect = new InetSocketAddress(host, port); + try { + socket.connect(addressToConnect, SOCKET_CREATION_TIMEOUT); + } catch (Exception ioe) { + return false; + } finally { + socket.close(); + } + return true; + } + }; + + RequestProcessor pingThread = new RequestProcessor("ping " + host + ":" + port, 1); // NOI18N + final Future task = pingThread.submit(checker); // NOI18N + + while (!task.isDone()) { + try { + task.get(500, TimeUnit.MILLISECONDS); + } catch (InterruptedException ex) { + Thread.interrupted(); + task.cancel(true); + } catch (ExecutionException ex) { + // normally should never happen + } catch (TimeoutException ex) { + // OK.. still be waiting + } + } + + boolean result = false; + + if (task.isDone()) { + try { + result = task.get(); + } catch (Exception ex) { + // normally should never happen + } + } + + return result; + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchDefaultAuthDataProvider.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchDefaultAuthDataProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchDefaultAuthDataProvider.java @@ -0,0 +1,89 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.nativeexecution.spi.AuthDataProvider.class, path = "ConnectionService/ssh", position = 10) +public final class JSchDefaultAuthDataProvider implements AuthDataProvider { + + @Override + @SuppressWarnings("unchecked") + public T getProperty(URI uri, Class clazz, final String name, Object... misc) { + if (String.class.equals(clazz) && AuthDataProvider.USERNAME.equals(name)) { + String userinfo = uri.getUserInfo(); + int pos = userinfo.indexOf(':'); + return (T) (pos < 0 ? userinfo : userinfo.substring(0, pos)); + } + + if (String.class.equals(clazz) && AuthDataProvider.HOST.equals(name)) { + return (T) uri.getHost(); + } + + if (Integer.class.equals(clazz) && AuthDataProvider.PORT.equals(name)) { + int port = uri.getPort(); + if (port <= 0) { + port = 22; + } + return (T) Integer.valueOf(port); + } + + if (char[].class.equals(clazz) && AuthDataProvider.PASSWORD.equals(name)) { + if ("password".equals(misc[0])) { + String userinfo = uri.getUserInfo(); + int pos = userinfo.indexOf(':'); + if (pos > 0) { + return (T) userinfo.substring(pos + 1).toCharArray(); + } + } + // TODO: Ask user ... + } + + return null; + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchProcessCreator.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchProcessCreator.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchProcessCreator.java @@ -0,0 +1,92 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; + +/** + * + * @author akrasny + */ +public final class JSchProcessCreator implements NativeProcessCreator { + + private final JSchConnection connectionImpl; + + public JSchProcessCreator(JSchConnection impl) { + this.connectionImpl = impl; + } + + @Override + public Map> getSupportedProperties() { + return JSchProcessParams.getSupportedProperties(); + } + + @Override + public NativeProcessImplementation createAndStart(NativeProcessParams params) throws NativeExecutionException { + if (params.getShellScript() == null && params.getCommand() == null) { + throw new NullPointerException("Command is not specified"); // NOI18N + } + + RemoteProcessImpl processImpl; + +// if (info.isPty()) { +// process = new PtyProcess(info); +// } else { + processImpl = new RemoteProcessImpl(connectionImpl, params); +// } + + + return processImpl.createAndStart(); + } + // protected T validatePropertyValue(String propertyName, T value) throws IllegalArgumentException { +// if (value instanceof Charset) { +// if (!Charset.isSupported(((Charset) value).name())) { +// throw new IllegalArgumentException("Unsupported charset " + ((Charset) value).name()); +// } +// } +// return value; +// } +} \ No newline at end of file diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchProcessParams.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchProcessParams.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchProcessParams.java @@ -0,0 +1,90 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; + +/** + * + * @author Andrew + */ +final class JSchProcessParams { + + private static final Map> props = new HashMap>(); + + static { + props.put("commandCharset", Charset.class); // NOI18N + props.put("outputCharset", Charset.class); // NOI18N + props.put("AgentForvarding", Boolean.class); // NOI18N + props.put("XForvarding", Boolean.class); // NOI18N + props.put("ptyMode", Boolean.class); // NOI18N + props.put("ptyType", String.class); // NOI18N + props.put("terminalMode", byte[].class); // NOI18N +// props.put("ptySize", Rectangle.class); // NOI18N + } + + static Map> getSupportedProperties() { + return Collections.unmodifiableMap(props); + } + + static Charset getCommandCharset(NativeProcessParams params) { + Charset charset = params.getProperty(Charset.class, "commandCharset"); // NOI18N + if (charset == null || !Charset.isSupported(charset.name())) { + charset = Charset.isSupported("UTF-8") // NOI18N + ? Charset.forName("UTF-8") : // NOI18N + Charset.defaultCharset(); // NOI18N + } + return charset; + } + + public boolean isXForvarding(NativeProcessParams params) { + return params.getProperty(Boolean.class, "XForvarding"); // NOI18N + } + + boolean isPty(NativeProcessParams params) { + return params.getProperty(Boolean.class, "ptyMode"); // NOI18N + } +} diff --git a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/JschSupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchSupport.java copy from dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/JschSupport.java copy to nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchSupport.java --- a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/JschSupport.java +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchSupport.java @@ -39,64 +39,76 @@ * * Portions Copyrighted 2010 Sun Microsystems, Inc. */ -package org.netbeans.modules.nativeexecution; +package org.netbeans.modules.nativeexecution.impl.jsch; import com.jcraft.jsch.Channel; import com.jcraft.jsch.ChannelExec; -import com.jcraft.jsch.ChannelShell; import com.jcraft.jsch.JSchException; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.logging.Level; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.support.Logger; +import java.util.logging.Logger; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.ExecutionLogger; /** * * @author ak119685 */ -public final class JschSupport { +public final class JSchSupport { private static final int JSCH_CONNECTION_TIMEOUT = Integer.getInteger("jsch.connection.timeout", 10000); // NOI18N - private final static java.util.logging.Logger log = Logger.getInstance(); + private final static Logger log = ExecutionLogger.getInstance(); - private JschSupport() { + private JSchSupport() { } /** - * Starts the specified command (executable + params) on the specified ExecutionEnvironment. + * Starts the specified command (executable + params) within the specified + * jsch connection. * - * @param env - environment to execute in + * @param connection - connection to execute in * @param command - executable + params to execute * @param params - (optional) channel params. May be null. - * @return I/O streams and opened execution JSch channel. Never returns NULL. + * @return I/O streams and opened execution JSch channel. Never returns + * NULL. * @throws IOException - if unable to aquire an execution channel - * @throws JSchException - if JSch exception occured + * @throws JSchException - if JSch exception occurred * @throws InterruptedException - if the thread was interrupted */ - public static ChannelStreams startCommand(final ExecutionEnvironment env, final String command, final ChannelParams params) - throws IOException, JSchException, InterruptedException { + public static ChannelStreams startCommand(final JSchConnection connection, final String command, final ChannelParams params) + throws NativeExecutionException { JSchWorker worker = new JSchWorker() { @Override - public ChannelStreams call() throws JSchException, IOException, InterruptedException { - ChannelExec echannel = (ChannelExec) ConnectionManagerAccessor.getDefault().openAndAcquireChannel(env, "exec", true); // NOI18N + public ChannelStreams call() throws NativeExecutionException { + ChannelExec echannel = connection.getChannelExec(true); if (echannel == null) { - throw new IOException("Cannot open exec channel on " + env + " for " + command); // NOI18N + throw new NativeExecutionException("Cannot open exec channel on " + connection.toString() + " for " + command); // NOI18N } echannel.setCommand(command); echannel.setXForwarding(params == null ? false : params.x11forward); - echannel.connect(JSCH_CONNECTION_TIMEOUT); - return new ChannelStreams(echannel, - echannel.getInputStream(), - echannel.getErrStream(), - echannel.getOutputStream()); + try { + echannel.connect(JSCH_CONNECTION_TIMEOUT); + } catch (JSchException ex) { + throw new NativeExecutionException(ex); + } + + ChannelStreams streams; + try { + streams = new ChannelStreams(echannel, + echannel.getInputStream(), + echannel.getErrStream(), + echannel.getOutputStream()); + } catch (IOException ex) { + throw new NativeExecutionException(ex); + } + + return streams; } @Override @@ -105,73 +117,74 @@ } }; - return start(worker, env, 2); + return start(worker, connection, 2); } - public static ChannelStreams startLoginShellSession(final ExecutionEnvironment env) throws IOException, JSchException, InterruptedException { - JSchWorker worker = new JSchWorker() { - - @Override - public ChannelStreams call() throws InterruptedException, JSchException, IOException { - ChannelShell shell = (ChannelShell) ConnectionManagerAccessor.getDefault().openAndAcquireChannel(env, "shell", true); // NOI18N - - if (shell == null) { - throw new IOException("Cannot open shell channel on " + env); // NOI18N - } - - shell.setPty(false); - shell.connect(JSCH_CONNECTION_TIMEOUT); - - return new ChannelStreams(shell, - shell.getInputStream(), - new ByteArrayInputStream(new byte[0]), - shell.getOutputStream()); - } - - @Override - public String toString() { - return "shell session for " + env.getDisplayName(); // NOI18N - } - }; - - return start(worker, env, 2); - } - - private synchronized static ChannelStreams start(final JSchWorker worker, final ExecutionEnvironment env, final int attempts) throws IOException, JSchException, InterruptedException { +// public static ChannelStreams startLoginShellSession(final JSchConnection connection) throws IOException, JSchException, InterruptedException { +// JSchWorker worker = new JSchWorker() { +// +// @Override +// public ChannelStreams call() throws InterruptedException, JSchException, IOException { +// ChannelShell shell = (ChannelShell) ConnectionManagerAccessor.getDefault().openAndAcquireChannel(env, "shell", true); // NOI18N +// +// if (shell == null) { +// throw new IOException("Cannot open shell channel on " + env); // NOI18N +// } +// +// shell.setPty(false); +// shell.connect(JSCH_CONNECTION_TIMEOUT); +// +// return new ChannelStreams(shell, +// shell.getInputStream(), +// new ByteArrayInputStream(new byte[0]), +// shell.getOutputStream()); +// } +// +// @Override +// public String toString() { +// return "shell session for " + env.getDisplayName(); // NOI18N +// } +// }; +// +// return start(worker, env, 2); +// } + private synchronized static ChannelStreams start(final JSchWorker worker, final JSchConnection connection, final int attempts) throws NativeExecutionException { int retry = attempts; while (retry-- > 0) { - try { - return worker.call(); - } catch (JSchException ex) { - String message = ex.getMessage(); - Throwable cause = ex.getCause(); - if (cause != null && cause instanceof NullPointerException) { - // Jsch bug... retry? - log.log(Level.INFO, "JSch exception opening channel to " + env + ". Retrying", ex); // NOI18N - } else if ("java.io.InterruptedIOException".equals(message)) { // NOI18N - log.log(Level.INFO, "JSch exception opening channel to " + env + ". Retrying in 0.5 seconds", ex); // NOI18N - try { - Thread.sleep(500); - } catch (InterruptedException ex1) { - Thread.currentThread().interrupt(); - break; - } - } else if ("channel is not opened.".equals(message)) { // NOI18N - log.log(Level.INFO, "JSch exception opening channel to " + env + ". Reconnecting and retrying", ex); // NOI18N - // Now reconnect disconnects old session and creates new, so this might help - ConnectionManagerAccessor.getDefault().reconnect(env); - - } else { - throw ex; - } - } catch (NullPointerException npe) { - // Jsch bug... retry? ;) - log.log(Level.FINE, "Exception from JSch", npe); // NOI18N - } +// try { + return worker.call(); +// } catch (NativeExecutionException ex) { +// if (ex.getCause() instanceof JSchException) { +// String message = ex.getMessage(); +// Throwable cause = ex.getCause(); +// if (cause != null && cause instanceof NullPointerException) { +// // Jsch bug... retry? +// log.log(Level.INFO, "JSch exception opening channel to " + env + ". Retrying", ex); // NOI18N +// } else if ("java.io.InterruptedIOException".equals(message)) { // NOI18N +// log.log(Level.INFO, "JSch exception opening channel to " + env + ". Retrying in 0.5 seconds", ex); // NOI18N +// try { +// Thread.sleep(500); +// } catch (InterruptedException ex1) { +// Thread.currentThread().interrupt(); +// break; +// } +// } else if ("channel is not opened.".equals(message)) { // NOI18N +// log.log(Level.INFO, "JSch exception opening channel to " + env + ". Reconnecting and retrying", ex); // NOI18N +// // Now reconnect disconnects old session and creates new, so this might help +// // ConnectionManagerAccessor.getDefault().reconnect(env); +// +// } else { +// throw ex; +// } +// } +// } catch (NullPointerException npe) { +// // Jsch bug... retry? ;) +// log.log(Level.FINE, "Exception from JSch", npe); // NOI18N +// } } - throw new IOException("Failed to execute " + worker.toString()); // NOI18N + throw new NativeExecutionException("Failed to execute " + worker.toString()); // NOI18N } public final static class ChannelStreams { @@ -201,6 +214,6 @@ private static interface JSchWorker { - T call() throws InterruptedException, IOException, JSchException; + T call() throws NativeExecutionException; } } diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteProcessImpl.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteProcessImpl.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteProcessImpl.java @@ -0,0 +1,192 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSchException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.ExecutionLogger; +import org.netbeans.modules.nativeexecution.impl.ExecutionLogger.Ref; +import org.netbeans.modules.nativeexecution.impl.common.AbstractNativeProcessImpl; +import org.netbeans.modules.nativeexecution.impl.jsch.JSchSupport.ChannelParams; +import org.netbeans.modules.nativeexecution.impl.jsch.JSchSupport.ChannelStreams; +import org.netbeans.modules.nativeexecution.impl.util.EnvironmentMapWriter; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; +import org.netbeans.modules.nativeexecution.util.SignalUtils; +import org.openide.util.Exceptions; + +/** + * + * @author akrasny + */ +final class RemoteProcessImpl extends AbstractNativeProcessImpl { + + private final JSchConnection connection; + private final Charset commandCharset; + private Channel channel; + private String displayName = ""; // NOI18N + + RemoteProcessImpl(JSchConnection connection, NativeProcessParams params) { + super(params); + this.connection = connection; + commandCharset = JSchProcessParams.getCommandCharset(params); + } + + @Override + protected ProcessStreams create(List command) throws NativeExecutionException { + displayName = Arrays.toString(command.toArray(new String[command.size()])); + return createImpl("exec " + commandToCommandLine(command)); // NOI18N + } + + @Override + protected ProcessStreams create(String shell, String script) throws NativeExecutionException { + displayName = script; + return createImpl(script + "; exit"); // NOI18N + } + + private ProcessStreams createImpl(String cmdLine) throws NativeExecutionException { + try { + ChannelParams params = new ChannelParams(); + ChannelStreams streams = JSchSupport.startCommand(connection, "/bin/sh -s", params); // NOI18N + channel = streams.channel; + OutputStreamWriter writer = new OutputStreamWriter(streams.in, commandCharset); + + // 1. get the PID of the shell + int token = 1000000 + new Random().nextInt(1000000); + write(writer, "echo " + token + "$$"); // NOI18N + + ProcessStreams result = new ProcessStreams(streams.out, streams.err, streams.in, readPID(token, streams.out)); + + final String workingDirectory = getParams().getWorkingDirectory(); + + // 2. cd to working directory + if (workingDirectory != null) { + write(writer, "cd \"" + workingDirectory + "\" || exit 1"); // NOI18N + } + + if (!channel.isConnected()) { + // Wrong directory ... + return result; + } + + // 3. set environment + EnvironmentMapWriter.write(writer, getParams().getEnvironment()); + + // 5. finally do exec + write(writer, cmdLine); + + return result; + } catch (IOException ex) { + throw new NativeExecutionException(ex); + } + } + + @Override + public int waitResult() throws InterruptedException { + Ref logRef = ExecutionLogger.getInstance().reportStart(toString()); + try { + while (channel.isConnected()) { + Thread.sleep(200); + } + + return channel.getExitStatus(); + } finally { + try { + connection.releaseChannel(channel); + } catch (JSchException ex) { + Exceptions.printStackTrace(ex); + } + ExecutionLogger.getInstance().reportDone(logRef); + } + } + + private int readPID(int token, final InputStream is) throws IOException { + int c, pid = 0; + boolean found = false; + + while (true) { + c = is.read(); + + if (c >= '0' && c <= '9') { + pid = pid * 10 + (c - '0'); + if (pid == token) { + pid = 0; + found = true; + } + } else { + if (found) { + break; + } + } + } + + return pid; + } + + private void write(OutputStreamWriter writer, String data) throws IOException { + writer.write(data); + writer.write("\n"); + writer.flush(); + } + + @Override + public void destroy() { + try { + channel.sendSignal(SignalUtils.Signal.SIGINT.name()); + } catch (Exception ex) { + // Exceptions.printStackTrace(ex); + } + channel.disconnect(); + } + + @Override + public String toString() { + return displayName; + } +} diff --git a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/support/RemoteUserInfo.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteUserInfo.java copy from dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/support/RemoteUserInfo.java copy to nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteUserInfo.java --- a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/support/RemoteUserInfo.java +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteUserInfo.java @@ -39,143 +39,89 @@ * * Portions Copyrighted 2009 Sun Microsystems, Inc. */ -package org.netbeans.modules.nativeexecution.support; +package org.netbeans.modules.nativeexecution.impl.jsch; import com.jcraft.jsch.UIKeyboardInteractive; import com.jcraft.jsch.UserInfo; -import java.awt.Component; +import java.net.URI; import java.util.Arrays; -import java.util.concurrent.CancellationException; -import javax.swing.JOptionPane; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.util.PasswordManager; -import org.netbeans.modules.nativeexecution.support.ui.CertPassphraseDlg; -import org.netbeans.modules.nativeexecution.support.ui.PasswordDlg; -import org.openide.util.Mutex; +import javax.security.auth.callback.PasswordCallback; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; import org.openide.util.NbBundle; -import org.openide.windows.WindowManager; final public class RemoteUserInfo implements UserInfo, UIKeyboardInteractive { - private final static Object lock = RemoteUserInfo.class.getName() + "Lock"; // NOI18N - private final static PasswordManager pm = PasswordManager.getInstance(); - private final Component parent; - private final ExecutionEnvironment env; - private volatile Component parentWindow = null; - private String passphrase = null; - private volatile String password = null; - private final boolean allowInterraction; + private final AuthDataProvider dataProvider; + private PasswordCallback callback; + private final URI uri; - public RemoteUserInfo(ExecutionEnvironment env, boolean allowToAskForPassword) { - this.env = env; - this.allowInterraction = allowToAskForPassword; - Mutex.EVENT.readAccess(new Runnable() { - - @Override - public void run() { - parentWindow = WindowManager.getDefault().getMainWindow(); - } - }); - parent = parentWindow; + public RemoteUserInfo(URI uri, AuthDataProvider dataProvider) { + this.uri = uri; + this.dataProvider = dataProvider; } @Override public String getPassphrase() { - String result = passphrase; - // Never store passphrase in memory - // Only for short a period. Between prompt and following get. - passphrase = null; - return result; + if (callback == null || callback.getPassword() == null) { + return null; + } + + char[] pwd = callback.getPassword(); + // jsch interface requires a String object ;( + String passphrase = new String(pwd); + Arrays.fill(pwd, 'x'); + return passphrase; } @Override public String getPassword() { - char[] saved = pm.getPassword(env); - if (saved != null) { - return new String(saved); + if (callback == null || callback.getPassword() == null) { + return null; } - String result = password; - // Never store password in memory - // Only for short a period. Between prompt and following get. - password = null; - return result; + char[] pwd = callback.getPassword(); + // jsch interface requires a String object ;( + String password = new String(pwd); + Arrays.fill(pwd, 'x'); + return password; } @Override public boolean promptPassword(String message) { - synchronized (lock) { - if (pm.getPassword(env) != null) { - return true; - } + callback = new PasswordCallback(message, false); + char[] pwd = dataProvider.getProperty(uri, char[].class, AuthDataProvider.PASSWORD, "password", message); // NOI18N + if (pwd != null) { + callback.setPassword(pwd); + return true; + } - if (!allowInterraction) { - return false; - } - - boolean result; - - PasswordDlg pwdDlg = new PasswordDlg(); - - synchronized (lock) { - result = pwdDlg.askPassword(env); - } - - if (result) { - char[] clearPassword = pwdDlg.getPassword(); - pm.storePassword(env, clearPassword, pwdDlg.isRememberPassword()); - Arrays.fill(clearPassword, (char) 0); - pwdDlg.clearPassword(); - return true; - } else { - throw new CancellationException(loc("USER_AUTH_CANCELED")); // NOI18N - } - } + return false; } @Override - public boolean promptPassphrase(String arg0) { - if (!allowInterraction) { - return false; + public boolean promptPassphrase(String message) { + char[] pwd = dataProvider.getProperty(uri, char[].class, AuthDataProvider.PASSWORD, "passphrase", message); // NOI18N + if (pwd != null) { + callback.setPassword(pwd); + return true; } - synchronized (lock) { - boolean result; - - CertPassphraseDlg pwdDlg = new CertPassphraseDlg(); - - result = pwdDlg.askPassword(env, arg0); - - if (result) { - passphrase = new String(pwdDlg.getPassword()); - pwdDlg.clearPassword(); - return true; - } else { - throw new CancellationException(loc("USER_AUTH_CANCELED")); // NOI18N - } - } + return false; } @Override - public boolean promptYesNo(String str) { - Object[] options = {"yes", "no"}; // NOI18N - int foo; - - synchronized (lock) { - foo = JOptionPane.showOptionDialog(parent, str, - loc("TITLE_YN_Warning"), // NOI18N - JOptionPane.DEFAULT_OPTION, - JOptionPane.WARNING_MESSAGE, null, options, options[0]); - } - - return foo == 0; + public boolean promptYesNo(String message) { + NotifyDescriptor nd = new NotifyDescriptor.Confirmation(message, NbBundle.getMessage(RemoteUserInfo.class, "TITLE_YN_Warning")); + DialogDisplayer.getDefault().notify(nd); + return nd.getValue() == NotifyDescriptor.YES_OPTION; } @Override public void showMessage(String message) { - synchronized (lock) { - JOptionPane.showMessageDialog(parent, message); - } + NotifyDescriptor nd = new NotifyDescriptor.Message(message); + DialogDisplayer.getDefault().notify(nd); } @Override @@ -187,7 +133,7 @@ if (prompt.length == 1 && !echo[0]) { // this is a password request - if (!promptPassword(loc("MSG_PasswordInteractive", // NOI18N + if (!promptPassword(NbBundle.getMessage(RemoteUserInfo.class, "MSG_PasswordInteractive", // NOI18N destination, prompt[0]))) { return null; } else { @@ -203,8 +149,4 @@ return null; } } - - private static String loc(String key, String... params) { - return NbBundle.getMessage(RemoteUserInfo.class, key, params); - } } diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalConnection.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalConnection.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalConnection.java @@ -0,0 +1,110 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.local; + +import java.net.InetAddress; +import java.net.URI; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.common.ShellBasedConnectionInfoProvider; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author akrasny + */ +public final class LocalConnection implements ConnectionImplementation { + + private static final LocalConnection instance = new LocalConnection(); + private final Lookup lookup; + private final URI uri; + + private LocalConnection() { + lookup = Lookups.fixed(ShellBasedConnectionInfoProvider.getInstance()); + URI localuri = null; + try { + String user = System.getProperty("user.name"); // NOI18N + InetAddress addr = InetAddress.getLocalHost(); + String hostname = addr.getHostName(); + localuri = new URI("localhost://" + user + "@" + hostname); // NOI18N + } catch (Exception ex) { + throw new InternalError("Unable to detect localhost's URI"); // NOI18N + } finally { + uri = localuri; + } + } + + static LocalConnection getInstance() { + return instance; + } + + @Override + public boolean isConnected() { + return true; + } + + @Override + public NativeProcessCreator newProcessBuilderImpl() throws NativeExecutionException { + return new LocalProcessCreator(); + } + + @Override + public String toString() { + return "localhost"; + } + + @Override + public Lookup getLookup() { + return lookup; + } + + @Override + public void disconnect() { + } + + @Override + public URI getURI() { + return uri; + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalConnector.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalConnector.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalConnector.java @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.local; + +import java.net.URI; +import java.net.URISyntaxException; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.jsch.JSchConnector; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.ConnectorImplementation; +import org.netbeans.modules.nativeexecution.util.URIMatcher; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.nativeexecution.spi.ConnectorImplementation.class, path = "ConnectionService/localhost", position = 10) +public final class LocalConnector implements ConnectorImplementation { + + @Override + public ConnectionImplementation connectTo(final URI uri, final AuthDataProvider authDataProvider) throws InterruptedException, NativeExecutionException { + LocalConnection local = LocalConnection.getInstance(); + + if (new URIMatcher(local.getURI()).isIdenticalURI(uri)) { + return local; + } + + try { + // TODO: Maybe ask user what to do... + URI remoteSSH = new URI("ssh://" + uri.getUserInfo() + "@" + uri.getHost() + ":22"); // NOI18N + JSchConnector jschConnector = Lookups.forPath("ConnectionService/ssh").lookup(JSchConnector.class); // NOI18N + return jschConnector.connectTo(remoteSSH, authDataProvider); + } catch (URISyntaxException ex) { + } + + return null; + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessCreator.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessCreator.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessCreator.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.local; + +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; + +/** + * + * @author akrasny + */ +final class LocalProcessCreator implements NativeProcessCreator { + + @Override + public Map> getSupportedProperties() { + return LocalProcessParams.getSupportedProperties(); + } + + @Override + public NativeProcessImplementation createAndStart(NativeProcessParams params) throws NativeExecutionException { + if (params.getShellScript() == null && params.getCommand() == null) { + throw new NullPointerException("Command is not specified"); // NOI18N + } + return new LocalProcessImpl(params).createAndStart(); + } +} \ No newline at end of file diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessImpl.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessImpl.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessImpl.java @@ -0,0 +1,119 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.local; + +import com.sun.jna.Pointer; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.common.AbstractNativeProcessImpl; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; + +/** + * + * @author akrasny + */ +final class LocalProcessImpl extends AbstractNativeProcessImpl { + + private Process process; + + LocalProcessImpl(final NativeProcessParams params) { + super(params); + } + + @Override + protected ProcessStreams create(List command) throws NativeExecutionException { + ProcessBuilder pb = new ProcessBuilder(command); + Map env = getParams().getEnvironment(); + if (!env.isEmpty()) { + Map penv = pb.environment(); + penv.clear(); + penv.putAll(env); + } + try { + process = pb.start(); + } catch (IOException ex) { + throw new NativeExecutionException(ex); + } + + int pid = -1; + + try { + String className = process.getClass().getName(); + if ("java.lang.Win32Process".equals(className) || "java.lang.ProcessImpl".equals(className)) { // NOI18N + Field f = process.getClass().getDeclaredField("handle"); // NOI18N + f.setAccessible(true); + long phandle = f.getLong(process); + + Win32APISupport kernel = Win32APISupport.instance; + Win32APISupport.HANDLE handle = new Win32APISupport.HANDLE(); + handle.setPointer(Pointer.createConstant(phandle)); + pid = kernel.GetProcessId(handle); + } else if ("java.lang.UNIXProcess".equals(className)) { // NOI18N + Field f = process.getClass().getDeclaredField("pid"); // NOI18N + f.setAccessible(true); + pid = f.getInt(process); + } + } catch (Throwable e) { + } + + return new ProcessStreams(process.getInputStream(), process.getErrorStream(), process.getOutputStream(), pid); + } + + @Override + protected ProcessStreams create(String shell, String script) throws NativeExecutionException { + return create(Arrays.asList(shell, "-c", script)); // NOI18N + } + + @Override + public void destroy() { + process.destroy(); + } + + @Override + public int waitResult() throws InterruptedException { + return process.waitFor(); + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessParams.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessParams.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessParams.java @@ -0,0 +1,68 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.local; + +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author akrasny + */ +final class LocalProcessParams { + + private static final Map> props = new HashMap>(); + + static { + props.put("commandCharset", Charset.class); // NOI18N + props.put("outputCharset", Charset.class); // NOI18N + props.put("ptyMode", Boolean.class); // NOI18N + props.put("ptyType", String.class); // NOI18N + props.put("terminalMode", byte[].class); // NOI18N + } + + public static Map> getSupportedProperties() { + return Collections.unmodifiableMap(props); + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalURIIdentifier.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalURIIdentifier.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalURIIdentifier.java @@ -0,0 +1,78 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.local; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.spi.URIIdentifier; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.nativeexecution.spi.URIIdentifier.class, path = "ConnectionService/localhost", position = 100) +public class LocalURIIdentifier implements URIIdentifier { + + private final URI localhost = LocalConnection.getInstance().getURI(); + + @Override + public boolean areIdentical(URI uri1, URI uri2) { + String host1 = uri1.getHost(); + String host2 = uri2.getHost(); + if (host1 == null) { + host1 = localhost.getHost(); + } + if (host2 == null) { + host2 = localhost.getHost(); + } + + String user1 = uri1.getUserInfo(); + String user2 = uri2.getUserInfo(); + if (user1 == null) { + user1 = localhost.getUserInfo(); + } + if (user2 == null) { + user2 = localhost.getUserInfo(); + } + return host1.equals(host2) && user1.equals(user2); + } +} diff --git a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/support/Win32APISupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/Win32APISupport.java copy from dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/support/Win32APISupport.java copy to nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/Win32APISupport.java --- a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/support/Win32APISupport.java +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/Win32APISupport.java @@ -39,7 +39,7 @@ * * Portions Copyrighted 2009 Sun Microsystems, Inc. */ -package org.netbeans.modules.nativeexecution.support; +package org.netbeans.modules.nativeexecution.impl.local; import com.sun.jna.FromNativeContext; import com.sun.jna.Native; diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/resources/Bundle.properties b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/resources/Bundle.properties new file mode 100755 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/resources/Bundle.properties @@ -0,0 +1,2 @@ +OpenIDE-Module-Name=Native Execution Implementation + diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/resources/hostinfo.sh b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/resources/hostinfo.sh new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/resources/hostinfo.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin +HOSTNAME=`uname -n` +OS=`uname -s` +CPUTYPE=`uname -p` +BITNESS=32 + +LS=/bin/ls +OSFAMILY= +DATETIME=`date -u +'%Y-%m-%d %H:%M:%S'` + +if [ "${OS}" = "SunOS" ]; then + BITNESS=`isainfo -b` + OSFAMILY="SOLARIS" + OSNAME="SunOS" + OSBUILD=`head -1 /etc/release | sed -e "s/^ *//"` + CPUNUM=`/usr/sbin/psrinfo -v | grep "^Status of" | wc -l | sed 's/^ *//'` +else + if [ "${OS}" = "Darwin" ]; then + sysctl hw.cpu64bit_capable | grep -q "1$" + if [ $? -eq 0 ]; then + BITNESS=64 + fi + else + uname -a | egrep "x86_64|WOW64" >/dev/null + if [ $? -eq 0 ]; then + BITNESS=64 + fi + fi + + if [ -f "/etc/sun-release" ]; then + OSNAME="${OS}-JDS" + OSBUILD=`head -1 /etc/sun-release` + elif [ -f /etc/SuSE-release ]; then + OSNAME="${OS}-SuSE" + OSBUILD=`cat /etc/SuSE-release | tr "\n" " "`; + elif [ -f /etc/redhat-release ]; then + OSNAME="${OS}-Redhat" + OSBUILD=`head -1 /etc/redhat-release` + elif [ -f /etc/gentoo-release ]; then + OSNAME="${OS}-Gentoo" + OSBUILD=`head -1 /etc/gentoo-release` + elif [ -f /etc/lsb-release ]; then + OSNAME="${OS}-"`cat /etc/lsb-release | grep DISTRIB_ID | sed 's/.*=//'` + OSBUILD=`cat /etc/lsb-release | grep DISTRIB_DESCRIPTION | sed 's/.*=//' | sed 's/"//g'` + fi +fi + +OSFAMILY=${OSFAMILY:-`echo ${OS} | grep _NT- >/dev/null && echo WINDOWS`} +OSFAMILY=${OSFAMILY:-`test "$OS" = "Darwin" && echo MACOSX`} +OSFAMILY=${OSFAMILY:-`test "$OS" = "Linux" && echo LINUX`} +OSFAMILY=${OSFAMILY:-${OS}} + +CPUFAMILY=`(echo ${CPUTYPE} | egrep "^i|x86_64|athlon|Intel" >/dev/null && echo x86) || echo ${CPUTYPE}` +if [ "${CPUFAMILY}" != "x86" -a "${CPUFAMILY}" != "sparc" ]; then + CPUTYPE=`uname -m` +fi +CPUFAMILY=`(echo ${CPUTYPE} | egrep "^i|x86_64|athlon|Intel" >/dev/null && echo x86) || echo ${CPUTYPE}` + +USERDIRBASE=${HOME} + +if [ "${OSFAMILY}" = "LINUX" ]; then + CPUNUM=`cat /proc/cpuinfo | grep processor | wc -l | sed 's/^ *//'` +elif [ "${OSFAMILY}" = "WINDOWS" ]; then + CPUNUM=$NUMBER_OF_PROCESSORS + OSNAME=`uname` + USERDIRBASE=${USERPROFILE} +elif [ "${OSFAMILY}" = "MACOSX" ]; then + CPUNUM=`hostinfo | awk '/processor.*logical/{print $1}'` + OSNAME="MacOSX" + OSBUILD=`hostinfo | sed -n '/kernel version/{n;p;}' | sed 's/[ ]*\([^:]*\).*/\1/'` +fi + +USER=${USER:-`logname 2>/dev/null`} +USER=${USER:-${USERNAME}} +TMPBASE=${TMPBASE:-/var/tmp} + +SUFFIX=0 +TMPDIRBASE=${TMPBASE}/nbexec_${USER} +mkdir -p ${TMPDIRBASE} +while [ ! -w ${TMPDIRBASE} -a ${SUFFIX} -lt 5 ]; do + echo "Warning: ${TMPDIRBASE} is not writable">&2 + SUFFIX=`expr 1 + ${SUFFIX}` + TMPDIRBASE=${TMPBASE}/nbexec_${USER}_${SUFFIX} + /bin/mkdir -p ${TMPDIRBASE} 2>/dev/null +done + +if [ -w ${TMPDIRBASE} ]; then + SUFFIX=0 + TMPBASE=${TMPDIRBASE} + TMPDIRBASE=${TMPBASE}/${NB_KEY} + mkdir -p ${TMPDIRBASE} + while [ ! -w ${TMPDIRBASE} -a ${SUFFIX} -lt 5 ]; do + echo "Warning: ${TMPDIRBASE} is not writable">&2 + SUFFIX=`expr 1 + ${SUFFIX}` + TMPDIRBASE=${TMPBASE}/${NB_KEY}_${SUFFIX} + /bin/mkdir -p ${TMPDIRBASE} 2>/dev/null + done +fi + +if [ ! -w ${TMPDIRBASE} ]; then + TMPDIRBASE=${TMPBASE} +fi + +if [ ! -w ${TMPDIRBASE} ]; then + echo "Error: {TMPDIRBASE} is not writable">&2 +fi + +ID=`LC_MESSAGES=C /usr/bin/id` + +echo +echo @@@@@INFO +echo BITNESS=${BITNESS} +echo CPUFAMILY=${CPUFAMILY} +echo CPUNUM=${CPUNUM} +echo CPUTYPE=${CPUTYPE} +echo HOSTNAME=${HOSTNAME} +echo OSNAME=${OSNAME} +echo OSBUILD=${OSBUILD} +echo OSFAMILY=${OSFAMILY} +echo USER=${USER} +echo SHELL=${SHELL} +echo USERDIRBASE=${USERDIRBASE} +echo TMPDIRBASE=${TMPDIRBASE} +echo DATETIME=${DATETIME} +echo ID=${ID} +echo @@@@@ENV +env +exit 0 diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/util/EnvironmentMapWriter.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/util/EnvironmentMapWriter.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/util/EnvironmentMapWriter.java @@ -0,0 +1,97 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.util; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.logging.Level; +import java.util.regex.Pattern; +import org.netbeans.modules.nativeexecution.impl.ExecutionLogger; + +/** + * + * @author akrasny + */ +public final class EnvironmentMapWriter { + + public static final HashSet varsNotForExport = new HashSet(Arrays.asList( + "SSH_AUTH_SOCK", "SSH_CONNECTION", "SSH_CLIENT", "SSH_TTY", "TZ", "HZ", "SHELL" // NOI18N + )); + + private EnvironmentMapWriter() { + } + + public static void write(OutputStreamWriter writer, Map environmentMap) throws IOException { + if (environmentMap.entrySet().isEmpty()) { + return; + } + // Very simple sanity check of vars... + Pattern pattern = Pattern.compile("[a-zA-Z0-9_]+"); // NOI18N + + for (Map.Entry entry : environmentMap.entrySet()) { + String name = entry.getKey(); + + if (varsNotForExport.contains(name)) { + continue; + } + + String value = entry.getValue(); + // check capitalized key by pattern + if (!pattern.matcher(name).matches()) { + ExecutionLogger.getInstance().log(Level.WARNING, + "Will not pass environment variable named {0} as it contains non alpha-numeric characters", name); // NOI18N + continue; + } + + if (value.indexOf('"') >= 0) { // NOI18N + value = value.replace("\"", "\\\""); // NOI18N + } + + writer.write(name + "=\"" + value + "\" && export " + name + "\n"); // NOI18N + } + + writer.flush(); + } +} diff --git a/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteProcessImplTest.java b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteProcessImplTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteProcessImplTest.java @@ -0,0 +1,148 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import java.net.URI; +import java.net.URISyntaxException; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.modules.nativeexecution.access.ConnectionAccessor; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.netbeans.modules.nativeexecution.util.ProcessUtils; +import org.openide.util.Exceptions; + +/** + * + * @author Andrew + */ +public class RemoteProcessImplTest { + + private URI remoteURI = null; + + public RemoteProcessImplTest() { + String host = System.getProperty("ne.remote.host"); // NOI18N + if (host != null) { + try { + remoteURI = new URI("ssh://tester@" + host + ":22"); // NOI18N + } catch (URISyntaxException ex) { + Exceptions.printStackTrace(ex); + } + } + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + @Test + public void testEnvironmentMap() throws Exception { + if (remoteURI == null) { + System.out.println("Skipped. To proceed, please use -Dne.remote.host= in test.run.args parameters list."); + System.out.println("It is expected that host can be logged in by user 'tester' with a password 'tester'."); + return; + } + + Connection c = null; + try { + c = ConnectionManager.connect(remoteURI, new AuthDataProviderImpl()); + NativeProcessBuilder pb = c.newProcessBuilder(); + EnvironmentMap env = pb.getEnvironmentMap(); + assertNotNull("EnvironmentMap is never NULL", env); + assertFalse("Environment map should not be empty", env.isEmpty()); + env.put("testEnvironmentMap", "some-value"); + + assertNull("testEnvironmentMap should NOT be in the map of other process builder", + c.newProcessBuilder().getEnvironmentMap().get("testEnvironmentMap")); + + assertTrue("testEnvironmentMap should be in the map", "some-value".equals(env.get("testEnvironmentMap"))); + assertTrue("testEnvironmentMap should be in the map", "some-value".equals(pb.getEnvironmentMap().get("testEnvironmentMap"))); + + pb.setCommand("env"); + ProcessUtils.ExitStatus status = ProcessUtils.execute(pb); + String data = "\n" + status.output + "\n"; + String[] split = data.split("\n"); + + assertTrue(Math.abs(100 - (split.length * 100 / env.size())) < 10); + assertTrue("testEnvironmentMap should be in the env", data.indexOf("\ntestEnvironmentMap=some-value\n") > 0); + } finally { + if (c != null) { + ConnectionAccessor.getDefault().getImpl(c).disconnect(); + } + } + } + + private static class AuthDataProviderImpl implements AuthDataProvider { + + @Override + public T getProperty(URI uri, Class clazz, String name, Object... misc) { + Object result = null; + + if (String.class.equals(clazz) && "KnownHostsFile".equals(name)) { + result = System.getProperty("user.home") + "/.ssh/known_hosts"; + } else if (char[].class.equals(clazz) && AuthDataProvider.PASSWORD.equals(name)) { + result = new char[]{'t', 'e', 's', 't', 'e', 'r'}; + } + + return clazz.cast(result); + } + } +} diff --git a/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessTest.java b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/local/LocalProcessTest.java @@ -0,0 +1,143 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.local; + +import java.net.URI; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.util.ProcessUtils; +import org.netbeans.modules.nativeexecution.util.ProcessUtils.ExitStatus; + +/** + * + * @author akrasny + */ +public class LocalProcessTest { + + public LocalProcessTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of create method, of class LocalProcess. + */ + @Test + public void testCreate() throws Exception { + Connection connection1 = ConnectionManager.getConnection(new URI("localhost")); + Connection connection2 = ConnectionManager.getConnection(connection1.getURI()); + Connection connection3 = ConnectionManager.getLocalConnection(); + + assertEquals(connection1, connection2); + assertEquals(connection2, connection3); + + assertTrue(connection1 == connection2); + assertTrue(connection2 == connection3); + + assertTrue(connection1.isConnected()); + assertTrue(connection2.isConnected()); + assertTrue(connection3.isConnected()); + } + + @Test + public void testEnvironmentMap() throws Exception { + Connection c = ConnectionManager.getLocalConnection(); + NativeProcessBuilder pb = c.newProcessBuilder(); + EnvironmentMap env = pb.getEnvironmentMap(); + assertNotNull("EnvironmentMap is never NULL", env); + assertFalse("Environment map should not be empty", env.isEmpty()); + env.put("testEnvironmentMap", "some-value"); + + assertNull("testEnvironmentMap should NOT be in the map of other process builder", + c.newProcessBuilder().getEnvironmentMap().get("testEnvironmentMap")); + + assertTrue("testEnvironmentMap should be in the map", "some-value".equals(env.get("testEnvironmentMap"))); + assertTrue("testEnvironmentMap should be in the map", "some-value".equals(pb.getEnvironmentMap().get("testEnvironmentMap"))); + + pb.setCommand("env"); + ExitStatus status = ProcessUtils.execute(pb); + String data = "\n" + status.output + "\n"; + String[] split = data.split("\n"); + + assertTrue(Math.abs(100 - (split.length * 100 / env.size())) < 10); + assertTrue("testEnvironmentMap should be in the env", data.indexOf("\ntestEnvironmentMap=some-value\n") > 0); + } +// +// @Test +// public void testEnvironmentMap() throws Exception { +// Connection c = ConnectionManager.getLocalConnection(); +// NativeProcessBuilder pb = c.newProcessBuilder().setCommand("/bin/ls", "/tmp"); +// ExitStatus result = ProcessUtils.execute(pb); +// System.out.println(result.output); +// +// pb.getEnvironmentMap().dump(System.out); +// System.out.println("----"); +// Map environment = new ProcessBuilder("").environment(); +// for (Map.Entry entry : environment.entrySet()) { +// System.out.println(entry.getKey() + " = " + entry.getValue()); +// } +// +// ConnectionManager.getLocalConnection(); +// } +} diff --git a/nativeexecution/arch.xml b/nativeexecution/arch.xml new file mode 100644 --- /dev/null +++ b/nativeexecution/arch.xml @@ -0,0 +1,1200 @@ + + +]> + + + + &api-questions; + + + + +

+ Native Execution API is intended to provide a flexible and extendable + aid for running external processes on a local or remote hosts. +

+

+ The module itself doesn't provide any specific implementation. It + also doesn't make any strict obligations on what is considered to be a + Process or a Host. But it has some assumptions regarging those + entities though... +

+

+ But first, two words about what this module is about and what it is not. + This module is NOT about hosts management nor it is about connections + management system (in sense of UI elements, persistance, etc.). This + module is almost entirely about establishing a connection to some + destination target (usually a host), running a process there and + getting back it's I/O. Also some basic utilities are provided that + could make these tasks easier to achieve. +

+

+ From the SPI implementor point of view this module is a set of + extension points to introduce own notion of Process/Connection + and add own implementation without a need to deal (a lot) with + threading and other annoying issues... +

+

+ This API/SPI was written while keeping in mind a previous experiense + with NativeExecution module. An attempt to re-design some questinoable + approaches used in the previous implementation has been made. +

+ Requirements for the module: +
    +
  • Extensibility - provide a way for adding new authorization + schemes, new transport layers +
  • +
  • Predictibility - no hidden acctions (like unexpected + connections initialization) +
  • +
  • Separatation between host (whatever it could be), user, + authorization way and transport +
  • +
  • Clear error-flow
  • +
  • Clear threading model
  • +
  • Clear control over streams encodings
  • +
  • Easy to use utilities
  • +
+

+ The entity a user of this API starts with is a Connection. + Any connection is identified by an URI. ConnectionManager is asked + to give a connection for some URI and this Connection becames a + provider of a NativeProcessBuilder which in turn can be used to + start a NativeProcess in the context of that Connection. +

+

+ A scheme of an URI passed to the ConnectionManager is used to lookup + an SPI implementor that can initiate, establish and provide a connection. + All SPI implementors that deal with particular 'kind' of connections + are registered in "ConnectionService/<scheme>" lookup path. +

+
+ + + + + +

+ There are already many users on an old API. The intention (and + success metric) is to move all client of the old API to this one. +
+ Unit tests will be provided. Also there should be no regression in + old API client's functionality. +

+
+ + + + + +

+ This module should became a public replacement for dlight.nativeexecution module. +
+ As API is changed all friend-modules should be modified to use this new one. + In most cases this should not be difficult, but taking into accont the + number of 'old' API clients (100+ friend modules) this task could take a while. + Estimation is two months. +

+
+ + + + + +

+ This module could be used for running external processes on a local and + remote hosts. It is extendable and SPI implementors could provide different + implementation of connections and processes... But at least two + implementations are to be provided together with this module (in a + separate implementation module). +
+ They are for starting local processes ("localhost" scheme) and remote + processes using Jsch services ("ssh" scheme). +
+ Common usage is: + +

+                try {
+                    URI uri = new URI("ssh://tester:tester@testmachine.com");
+                    Connection connection = ConnectionManager.connect(uri);
+
+                    NativeProcessBuilder npb = connection.newProcessBuilder();
+                    npb.setWorkingDirectory("/tmp/test").setCommand("my-command");
+                    npb.getEnvironmentMap().prependPath("PATH", "/path/to/my-command");
+
+                    ProcessUtils.ExitStatus result = ProcessUtils.execute(npb);
+                    if (result.isOK()) {
+                        System.out.println(result.output);
+                    }
+                } catch (NativeExecutionException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (URISyntaxException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+                
+ +

+
+ + + + + +

+ This module provides an API/SPI be used for running external + processes on a local or remote hosts. +

+
+ + + + + + + + + + + + +

+ Process-related API from dlight.nativeexecution is planned to be + first deprecated and then, when transition is done, demoved from + the product. +

+
+ + + + + +

+ Yes. Currently it has no UI except a ProgressBar which indicates a + connection to a remote host progress. +

+
+ + + + + +

+ N/A +

+
+ + + + + +

+ There is no any persistance in this module (at least for now). +
+ Module is extendable with SPI provided. +

+
+ + + + + +

+ jre 1.6+ +

+
+ + + + + +

+ JRE is enough. +

+
+ + + + + + + + + + + + +

+ none. The default implementation of remote processes support uses jsch. + But this is a different (implementation) module. +

+
+ + + + + +

+ This module is almos a pure API/SPI so it is platform-independent. +
+ Implementation module with support of Windows, Linux, Solaris and MacOS + will be provided. +

+
+ + + + + +

+ This module needs implementation of the "localhost" scheme support.
+ OpenIDE-Module-Needs: ConnectionService.localhost +

+
+ + + + + +

+ just JAR. +

+
+ + + + + +

+ Yes. +

+
+ + + + + +

+ There are several public API/SPI packages provided by this module. +

+
+ + + + + +

+ Installation location doesn't not matter, but as this module will + become a part of the ide cluster, most likely it will be in a shared + location. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ See ConnectionInfoProvider javadoc. +

+
+ + + + + +

+ There are connection-specific properties that could be of any kind. + SPI implementor defines pairs of property name -- class and later + API clients are expected to get/set defined properties of defined + classes. Runtime type checking is performed in put/get methods. See + javadoc for NativeProcessBuilder, AuthDataProvider. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Yes. This module is about starting external processes. It is up to + client to work with a result of execution. +

+
+ + + + + +

+ "ide.execution.logger.level" is used to define a logger level. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Some classes (most) are thread-safe, others are not. If class is not + thread-safe, it is described in a javadoc. +

+
+ + + +

+ N/A +

+
+ + + + + +

+ N/A +

+
+ + + + + +

+ N/A +

+
+ + + + + +

+ Yes, Lookup is in use. Connection has an associated Lookup that is + explored to get implementations of some specific services + (like ConnectionInfoProvider, SignalSupportProvider). + Also AuthDataProvider, URIIdentifier and Connector implementations + are registered in the global lookup within a "ConnectionService/<scheme>" + path. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ N/A +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Yes. They are run outside the ETD. +

+
+ + + + + +

+ N/A +

+
+ + + + + +

+ There is a code that can be plugged in and that can influence the + performance (like listeners implementations, as they are notified + synchroniously). But this is done outside the EDT...
+

+
+ + + + + +

+ No. +

+
+ + + + + +

+ Yes. There is a thread that is started once a second to test if all + known connections are still alive. +
+ This thread exists only if there are listeners that are waiting for + "connection lost" event and only if there is at least one active + connection. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ + + + + +

+ No. +

+
+ +
diff --git a/nativeexecution/build.xml b/nativeexecution/build.xml new file mode 100755 --- /dev/null +++ b/nativeexecution/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.nativeexecution + + diff --git a/nativeexecution/manifest.mf b/nativeexecution/manifest.mf new file mode 100755 --- /dev/null +++ b/nativeexecution/manifest.mf @@ -0,0 +1,7 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: false +OpenIDE-Module: org.netbeans.modules.nativeexecution +OpenIDE-Module-Implementation-Version: 1 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/nativeexecution/resources/Bundle.properties +OpenIDE-Module-Needs: ConnectionService.localhost + diff --git a/nativeexecution/nbproject/project.properties b/nativeexecution/nbproject/project.properties new file mode 100755 --- /dev/null +++ b/nativeexecution/nbproject/project.properties @@ -0,0 +1,5 @@ +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial +javadoc.arch=${basedir}/arch.xml +spec.version.base=1.0 + diff --git a/nativeexecution/nbproject/project.xml b/nativeexecution/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/nativeexecution/nbproject/project.xml @@ -0,0 +1,93 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.nativeexecution + + + org.netbeans.api.progress + + + + 1 + 1.28 + + + + org.openide.modules + + + + 7.32 + + + + org.openide.util + + + + 8.24 + + + + org.openide.util.lookup + + + + 8.15 + + + + + + unit + + org.netbeans.api.progress + + + + org.netbeans.libs.junit4 + + + + org.netbeans.modules.nativeexecution.impl + + + + + org.netbeans.modules.nbjunit + + + + + org.netbeans.modules.progress.ui + + + + + org.openide.dialogs + + + + org.openide.util + + + + + org.openide.util.lookup + + + + + + org.netbeans.modules.nativeexecution.api + org.netbeans.modules.nativeexecution.api.process + org.netbeans.modules.nativeexecution.spi + org.netbeans.modules.nativeexecution.spi.process + org.netbeans.modules.nativeexecution.spi.support + org.netbeans.modules.nativeexecution.util + + + + diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/ExecutionLogger.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/ExecutionLogger.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/ExecutionLogger.java @@ -0,0 +1,161 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution; + +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.StreamHandler; +import javax.swing.SwingUtilities; + +/** + * Logger that is used in the Execution's code. + * + * Has some utility methods... + * + * @author akrasny + */ +public final class ExecutionLogger extends Logger { + + private static final boolean assertionsEnabled; + private static final ExecutionLogger instance = new ExecutionLogger(); + + static { + boolean ea = false; + assert (ea = true); + assertionsEnabled = ea; + String level_str = System.getProperty("ide.execution.logger.level", "INFO").toUpperCase(); // NOI18N + Level level = Level.INFO; + + try { + level = Level.parse(level_str); + } catch (IllegalArgumentException ex) { + } + + instance.addHandler(new LoggerHandler()); + instance.setLevel(level); + } + private static final long startTimeMillis = System.currentTimeMillis(); + + private static class LoggerHandler extends StreamHandler { + + LoggerHandler() { + setOutputStream(System.out); + } + + @Override + public void publish(LogRecord record) { + String msg = getFormatter().format(record); + System.out.println("[" + (record.getMillis() - startTimeMillis) + " ms.] " + msg); // NOI18N + record.setMessage("[" + (record.getMillis() - startTimeMillis) + " ms.] " + record.getMessage()); // NOI18N + super.publish(record); + } + + @Override + public void flush() { + super.flush(); + } + + @Override + public void close() throws SecurityException { + } + } + + private ExecutionLogger() { + super("ide.execution.logger", null); // NOI18N + } + + public static ExecutionLogger getInstance() { + return instance; + } + + public static void assertTrue(boolean value) { + if (assertionsEnabled && !value) { + String message = "Assertion error"; // NOI18N + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertTrue(boolean value, String message) { + if (assertionsEnabled && !value) { + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertFalse(boolean value) { + if (assertionsEnabled && value) { + String message = "Assertion error"; // NOI18N + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertFalse(boolean value, String message) { + if (assertionsEnabled && value) { + instance.log(Level.SEVERE, message, new Exception(message)); + } + } + + public static void assertNonUiThread(String message) { + if (assertionsEnabled && SwingUtilities.isEventDispatchThread()) { + instance.log(Level.FINE, message, new Exception(message)); + } + } + + public static void assertNonUiThread() { + assertNonUiThread("Should not be called from UI thread"); // NOI18N + } + + public static void fullThreadDump(String title) { + final Set> stack = Thread.getAllStackTraces().entrySet(); + System.err.printf("----- %s Start Thread Dump-----\n", title == null ? "" : title); // NOI18N + for (Map.Entry entry : stack) { + System.err.println(entry.getKey().getName()); + for (StackTraceElement element : entry.getValue()) { + System.err.println("\tat " + element.toString()); // NOI18N + } + System.err.println(); + } + System.err.println("----- End Thread Dump-----"); // NOI18N + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/access/ConnectionAccessor.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/ConnectionAccessor.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/ConnectionAccessor.java @@ -0,0 +1,86 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.access; + +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.openide.util.Lookup; + +/** + * + * @author akrasny + */ +public abstract class ConnectionAccessor { + + private static volatile ConnectionAccessor DEFAULT; + + public static void setDefault(ConnectionAccessor accessor) { + if (DEFAULT != null) { + throw new IllegalStateException( + "ConnectionManagerAccessor is already defined"); // NOI18N + } + + DEFAULT = accessor; + } + + public static synchronized ConnectionAccessor getDefault() { + if (DEFAULT != null) { + return DEFAULT; + } + + try { + Class.forName(Connection.class.getName(), true, + Connection.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + } + + return DEFAULT; + } + + public abstract Connection createConnection(ConnectionImplementation impl); + + public abstract Lookup getConnectionLookup(Connection connection); + + public abstract void setImpl(Connection connection, ConnectionImplementation impl); + + public abstract ConnectionImplementation getImpl(Connection connection); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/access/EnvironmentMapAccessor.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/EnvironmentMapAccessor.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/EnvironmentMapAccessor.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.access; + +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; + +/** + * + * @author akrasny + */ +public abstract class EnvironmentMapAccessor { + + private static volatile EnvironmentMapAccessor DEFAULT; + + public static void setDefault(EnvironmentMapAccessor accessor) { + if (DEFAULT != null) { + throw new IllegalStateException( + "ConnectionManagerAccessor is already defined"); // NOI18N + } + + DEFAULT = accessor; + } + + public static synchronized EnvironmentMapAccessor getDefault() { + if (DEFAULT != null) { + return DEFAULT; + } + + try { + Class.forName(EnvironmentMap.class.getName(), true, + EnvironmentMap.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + } + + return DEFAULT; + } + + public abstract void store(EnvironmentMap envMap, Preferences envprop) throws BackingStoreException; + + public abstract EnvironmentMap load(Preferences envprop) throws BackingStoreException; +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessAccessor.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessAccessor.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessAccessor.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.access; + +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.process.NativeProcess; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; + +/** + * + * @author akrasny + */ +public abstract class NativeProcessAccessor { + + private static volatile NativeProcessAccessor DEFAULT; + + public static void setDefault(NativeProcessAccessor accessor) { + if (DEFAULT != null) { + throw new IllegalStateException( + "NativeProcessAccessor is already defined"); // NOI18N + } + + DEFAULT = accessor; + } + + public static synchronized NativeProcessAccessor getDefault() { + if (DEFAULT != null) { + return DEFAULT; + } + + try { + Class.forName(NativeProcess.class.getName(), true, + NativeProcess.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + } + + return DEFAULT; + } + + public abstract NativeProcessParams getProcessParams(final NativeProcess process); + + public abstract Connection getConnection(final NativeProcess process); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessBuilderFactory.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessBuilderFactory.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessBuilderFactory.java @@ -0,0 +1,80 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.access; + +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; + +/** + * + * @author akrasny + */ +public abstract class NativeProcessBuilderFactory { + + private static volatile NativeProcessBuilderFactory DEFAULT; + + public static void setDefault(NativeProcessBuilderFactory accessor) { + if (DEFAULT != null) { + throw new IllegalStateException( + "NativeProcessBuilderFactory is already defined"); // NOI18N + } + + DEFAULT = accessor; + } + + public static synchronized NativeProcessBuilderFactory getDefault() { + if (DEFAULT != null) { + return DEFAULT; + } + + try { + Class.forName(NativeProcessBuilder.class.getName(), true, + NativeProcessBuilder.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + } + + return DEFAULT; + } + + public abstract NativeProcessBuilder newProcessBuilder(NativeProcessCreator impl, Connection connection); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessParamsFactory.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessParamsFactory.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/access/NativeProcessParamsFactory.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.access; + +import java.util.List; +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; + +/** + * + * @author akrasny + */ +public abstract class NativeProcessParamsFactory { + + private static volatile NativeProcessParamsFactory DEFAULT; + + public static void setDefault(NativeProcessParamsFactory accessor) { + if (DEFAULT != null) { + throw new IllegalStateException( + "NativeProcessParamsFactory is already defined"); // NOI18N + } + + DEFAULT = accessor; + } + + public static synchronized NativeProcessParamsFactory getDefault() { + if (DEFAULT != null) { + return DEFAULT; + } + + try { + Class.forName(NativeProcessParams.class.getName(), true, + NativeProcessParams.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + } + + return DEFAULT; + } + + public abstract NativeProcessParams newNativeProcessParams(Connection connection, + Map connectionInfo, Map environmentMap, + Map properties, List command, String shell, + String shellScript, boolean redirectError, String wdir); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/BrokenConnectionException.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/BrokenConnectionException.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/BrokenConnectionException.java @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; + +/** + * Extension of the ConnectionException that is thrown when some action that + * requires an active connection is invoked on a dead one. + * + * @author akrasny + */ +public class BrokenConnectionException extends ConnectionException { + + /** + * Constructs a new BrokenConnectionException. + * + * @param uri an URI of the broken connection. + */ + public BrokenConnectionException(URI uri) { + super(uri, "HOST_NOT_CONNECTED"); // NOI18N + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/Bundle.properties b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/Bundle.properties new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/Bundle.properties @@ -0,0 +1,43 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2012 Oracle and/or its affiliates. All rights reserved. +# +# Oracle and Java are registered trademarks of Oracle and/or its affiliates. +# Other names may be trademarks of their respective owners. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. +# +# Contributor(s): +# +# Portions Copyrighted 2012 Sun Microsystems, Inc. + +ConnectionManager.progress.message=Connection progress +ConnectionManager.progress.connectTo=Connecting to {0} +ConnectionManager.progress.fetchingInfo=Getting connection info diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/Connection.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/Connection.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/Connection.java @@ -0,0 +1,167 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.access.ConnectionAccessor; +import org.netbeans.modules.nativeexecution.access.NativeProcessBuilderFactory; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.ConnectorImplementation; +import org.netbeans.modules.nativeexecution.spi.URIIdentifier; +import org.openide.util.Lookup; + +/** + * A connection (execution session). + * + *

Connection provides an access to a {@link ProcessBuilder} in the context + * of the associated session. One should use services provided by the + * {@link ConnectionManager} class to get a Connection.

+ * + * @author akrasny + */ +public final class Connection { + + private final Object implLock = new Object(); + private ConnectionImplementation impl; + + /** + * Constructs a new API object for provided implementation. + * + * @param uri an URI this connection is associated with. + */ + private Connection(ConnectionImplementation impl) { + this.impl = impl; + } + + /** + * Returns an actual URI this connection is associated with. + * + * @return an URI this connection is associated with. + * @see ConnectorImplementation + * @see URIIdentifier + */ + public URI getURI() { + synchronized (implLock) { + return impl.getURI(); + } + } + + /** + * Tests if the connection is alive. + * + * @return true if connection is established, false otherwise. + */ + public boolean isConnected() { + synchronized (implLock) { + return impl == null ? false : impl.isConnected(); + } + } + + /** + * Creates a new {@link NativeProcessBuilder} to be used for starting + * processes within this connection. + * + * @return a new not configured {@link NativeProcessBuilder}. + * @throws NativeExecutionException if some problem occurred + */ + public NativeProcessBuilder newProcessBuilder() throws NativeExecutionException { + synchronized (implLock) { + if (!isConnected()) { + throw new BrokenConnectionException(impl.getURI()); + } + + return NativeProcessBuilderFactory.getDefault().newProcessBuilder(impl.newProcessBuilderImpl(), this); + } + } + + /** + * Returns a human-readable string representation of the connection. + * + * @return a string representation of the connection. + */ + @Override + public String toString() { + synchronized (implLock) { + return impl.toString(); + } + } + + // + private static class ConnectionAccessorImpl extends ConnectionAccessor { + + @Override + public Lookup getConnectionLookup(Connection connection) { + ConnectionImplementation impl; + + synchronized (connection.implLock) { + impl = connection.impl; + } + + return impl == null ? Lookup.EMPTY : impl.getLookup(); + } + + @Override + public Connection createConnection(ConnectionImplementation impl) { + return new Connection(impl); + } + + @Override + public void setImpl(Connection connection, ConnectionImplementation impl) { + synchronized (connection.implLock) { + connection.impl = impl; + } + } + + @Override + public ConnectionImplementation getImpl(Connection connection) { + synchronized (connection.implLock) { + return connection.impl; + } + } + } + + static { + ConnectionAccessor.setDefault(new ConnectionAccessorImpl()); + } + // +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionCancelledException.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionCancelledException.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionCancelledException.java @@ -0,0 +1,62 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; + +/** + * Extension of the ConnectionException that denotes user-driven interruption of + * a connection process. + * + * @author akrasny + */ +public class ConnectionCancelledException extends ConnectionException { + + /** + * Constructs a new ConnectionCancelledException instance. + * + * @param uri an URI of the canceled connection. + */ + public ConnectionCancelledException(URI uri) { + super(uri, "CONNECTION_CANCELLED"); // NOI18N + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionException.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionException.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionException.java @@ -0,0 +1,96 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; + +/** + * This exception is thrown if any connection-related problem occurs. + * + * @author akrasny + */ +public class ConnectionException extends NativeExecutionException { + + private final URI uri; + + /** + * Constructs a ConnectionException with the specified detail message and + * nested exception. + * + * @param uri an URI of the connection. + * @param msg the detail message. + * @param cause the nested exception. + */ + public ConnectionException(URI uri, String msg, Throwable cause) { + super(msg, cause); + this.uri = uri; + } + + /** + * Constructs a ConnectionException with the specified detailed message. + * + * @param uri an URI of the connection. + * @param msg the detail message. + */ + public ConnectionException(URI uri, String msg) { + this(uri, msg, null); + } + + /** + * Constructs a ConnectionException with the specified nested exception. + * + * @param uri an URI of the connection + * @param cause the nested exception + */ + public ConnectionException(URI uri, Throwable cause) { + this(uri, cause == null ? null : cause.getMessage(), cause); + } + + /** + * Returns an URI of the connection destination. + * + * @return the URI of the connection destination. + */ + public final URI getURI() { + return uri; + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionManager.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionManager.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionManager.java @@ -0,0 +1,237 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.access.ConnectionAccessor; +import org.netbeans.modules.nativeexecution.api.ConnectionStateChangeEvent.State; +import org.netbeans.modules.nativeexecution.common.ConnectionListeners; +import org.netbeans.modules.nativeexecution.common.ConnectionsPool; +import org.netbeans.modules.nativeexecution.common.Connector; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.URIIdentifier; +import org.openide.util.Exceptions; + +/** + * An entry-point class for getting a {@link Connection} to the desired + * destination. + * + *

All ConnectionManager's methods are thread-safe.

+ * + * @author akrasny + */ +public final class ConnectionManager { + + private static final Connection LOCAL_CONNECTION; + + static { + Connection local = null; + try { + local = ConnectionManager.connect(new URI("localhost"), null); // NOI18N + } catch (Throwable th) { + Exceptions.printStackTrace(th); + } finally { + LOCAL_CONNECTION = local; + } + } + + private ConnectionManager() { + } + + /** + * Returns a connection to a local host. + * + *

This connection is always active (connected) and doesn't require any + * authorization.

+ * + * @return the connection to a localhost (current host, current user). + */ + public static Connection getLocalConnection() { + return LOCAL_CONNECTION; + } + + /** + * + * Returns previously established {@link Connection} identified by the same + * or equivalent URI. + * + *

This method is fast and could be invoked from the EDT.

+ * + *

There is a notion of equivalent URIs that could verbally differ, but + * denote the same destination. See {@link URIIdentifier}.

+ * + * + * @param uri the URI that identifies a connection to get. + * @return previously established active connection or {@code null}, if no + * active connection exists. + * + * @see URIIdentifier + */ + public static Connection getConnection(final URI uri) { + Connection connection = ConnectionsPool.get(uri); + + if (connection == null || !connection.isConnected()) { + return null; + } + + return connection; + } + + /** + * An implicit request to establish a connection with a destination + * identified by the provided URI. + * + *

This method acts like {@code connect(uri, null)}

+ * + * @param uri the URI of a desired connection destination. + * @see #connect(URI, AuthDataProvider) + * @throws NativeExecutionException if connection process was canceled or + * failed by any other reason. + */ + public static Connection connect(URI uri) throws NativeExecutionException { + return connect(uri, null); + } + + /** + * An implicit request to establish a connection with the destination + * identified by the provided URI using provided additional source of + * authentication data. + * + *

No new connection is established if a connection identified by this or + * equivalent URI already exists. The existent one is returned instead.

+ * + *

Infrastructure asks passed {@link AuthDataProvider} to provide any + * authentication data needed for connection establishment. In case no + * requested info is provided by the passed provider, the default one + * (supplied by an SPI implementor) is queried. Each of these providers + * could initiate user interaction in order to get needed information.

+ * + *

This method could be called from any thread (including ETD). In latter + * case a modal dialog with a progress bar is used to avoid UI + * deadlocks.

+ * + *

Note that provider may return a connection with an URI that is + * absolutely different from the passed one. This could happen, for example, + * when request connection to an URI that doesn't precisely identifies the + * final destination - like {@code ssh://testhost}. In this case + * AuthDataProvider could be asked to provide a user name and the URI of a + * connection could be changed to {@code ssh://user@testhost}.

+ * + * @param uri the URI of a desired connection destination. + * @param authProvider provider of authentication data needed for connection + * establishment. + * + * @throws NativeExecutionException if connection process was canceled or + * failed by any other reason. + */ + public static Connection connect(URI uri, AuthDataProvider authProvider) throws NativeExecutionException { + Connection connection = ConnectionsPool.get(uri); + + if (connection != null && connection.isConnected()) { + return connection; + } + + ConnectionAccessor access = ConnectionAccessor.getDefault(); + ConnectionImplementation impl = Connector.connect(uri, authProvider); + assert impl != null; + + if (connection == null) { + connection = access.createConnection(impl); + ConnectionsPool.add(connection); + } else { + access.setImpl(connection, impl); + } + + ConnectionStateChangeEvent ev = new ConnectionStateChangeEvent(connection.getURI(), State.CONNECTION_ESTABLISHED); + ConnectionListeners.notifyListeners(connection.getURI(), ev); + return connection; + } + + /** + * An implicit request to close previously established connection. + * + * @param c Connection to terminate + */ + static void disconnect(Connection c) { + if (!c.isConnected()) { + return; + } + + ConnectionListeners.stopTrackingTask(); + ConnectionAccessor access = ConnectionAccessor.getDefault(); + access.getImpl(c).disconnect(); + if (!c.isConnected()) { + ConnectionStateChangeEvent ev = new ConnectionStateChangeEvent(c.getURI(), State.CONNECTION_CLOSED); + ConnectionListeners.notifyListeners(c.getURI(), ev); + } + } + + /** + * Adds a listener to state changes of a connection to a given URI. + * + *

It permits you to listen to a connection which is not established + * yet, or continue listening to it after it is disconnected, reconnected, + * etc. + *

+ *

A listener can listen to any number of URIs. Note that listeners are + * always held weakly - if the listener is collected, it is quietly + * removed.

+ * + * @param listener ConnectionStateChangeListener to listen to changes in + * Connection state. + * @param uri URI of the Connection target (even not connected yet existing) + */ + public static void addConnectionStateChangeListener(ConnectionStateChangeListener listener, URI uri) { + ConnectionListeners.addConnectionStateChangeListener(listener, uri); + } + + /** + * Removes a listener to state changes of a given connection destination. + * + * @param listener ConnectionStateChangeListener to be removed + * @param uri URI of the Connection target + */ + public static void removeConnectionStateChangeListener(ConnectionStateChangeListener listener, URI uri) { + ConnectionListeners.removeConnectionStateChangeListener(listener, uri); + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionStateChangeEvent.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionStateChangeEvent.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionStateChangeEvent.java @@ -0,0 +1,113 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; +import java.util.EventObject; + +/** + * An Event object that provides information about the source of a + * connection-related event. + * + *

+ * {@link ConnectionStateChangeEvent}s are sent to registered {@link ConnectionStateChangeListener}s + * when status of some connection changes.

+ * + * @see ConnectionManager + * + * @author akrasny + */ +public final class ConnectionStateChangeEvent extends EventObject { + + private static final long serialVersionUID = 1L; + private final State state; + + /** + * Constructs a new {@link ConnectionStateChangeEvent}. + * + * @param uri an URI of the connection this event belongs to. + * @param state the new {@link State} of the connection. + */ + public ConnectionStateChangeEvent(URI uri, State state) { + super(uri); + this.state = state; + } + + /** + * Returns a {@link State} this event was generated for. + * + * @return the {@link State} of the connection this event was issued for. + */ + public State getState() { + return state; + } + + /** + * Returns the {@link URI} this event relates to. + * + * @return the related {@link URI}. + */ + @Override + public URI getSource() { + return (URI) super.getSource(); + } + + /** + * Enumeration of possible {@link State}s that events are generated for. + */ + public enum State { + + /** + * Issued when a new connection is established. + */ + CONNECTION_ESTABLISHED, + /** + * Issued when a connection is implicitly terminated. + */ + CONNECTION_CLOSED, + /** + * Issued when infrastructure detects that connection is not active + * while no implicit termination was performed. + */ + CONNECTION_LOST + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionStateChangeListener.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionStateChangeListener.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/ConnectionStateChangeListener.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.util.EventListener; + +/** + * Used to be notified about active connections state changes. + * + * + * @author akrasny + */ +public interface ConnectionStateChangeListener extends EventListener { + + /** + * Is invoked on connection state change. + * + *

It is guaranteed that events are delivered in the same order they + * occur.

+ * + *

For implicit actions (connect/disconnect) events are sent + * synchronously and instantly. But there is an event that is asynchronous + * by it's nature - it is when connection is lost. In this case there could + * be a delay in event delivery until after infrastructure detects a broken + * connection.

+ * + * @param event the event that describes a state change. + */ + public void connectionStateChanged(ConnectionStateChangeEvent event); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/DestinationUnreachableException.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/DestinationUnreachableException.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/DestinationUnreachableException.java @@ -0,0 +1,63 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; + +/** + * Extension of the ConnectionException that denotes that connection target + * point is not accessible. + * + * @author akrasny + */ +public class DestinationUnreachableException extends ConnectionException { + + /** + * Constructs a new DestinationUnreachableException exception with the + * destination URI. + * + * @param uri an URI of the unreachable destination. + */ + public DestinationUnreachableException(URI uri) { + super(uri, "DESTINATION_UNREACHABLE"); // NOI18N + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/EnvironmentMap.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/EnvironmentMap.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/EnvironmentMap.java @@ -0,0 +1,239 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import org.netbeans.modules.nativeexecution.access.EnvironmentMapAccessor; +import org.netbeans.modules.nativeexecution.util.MacroMap; +import org.openide.util.Exceptions; + +/** + * An extension of a {@link MacroMap} extended with methods to work on PATH-like + * variables. + * + *

This collection is NOT thread-safe.

+ * + * @author akrasny + */ +public final class EnvironmentMap extends MacroMap implements Cloneable { + + private final char pathSeparator; + private final Mode mode; + + private EnvironmentMap(Mode mode, char pathSeparator, Comparator comparator) { + super(comparator); + this.mode = mode; + this.pathSeparator = pathSeparator; + } + + private EnvironmentMap(Mode mode, MacroMap map, char pathSeparator) { + super(map); + this.mode = mode; + this.pathSeparator = pathSeparator; + } + + /** + * Returns a new empty {@link EnvironmentMap} with a specified {@code pathSeparator} + * to be used when dealing with PATH-like variables and that uses a + * case-sensitive algorithm for comparing variable names. + * + * @param pathSeparator a char to be used as a separator when dealing with + * PATH-like variables. + * @return new empty case-sensitive {@link EnvironmentMap}. + */ + public static EnvironmentMap createCaseSensitive(char pathSeparator) { + return new EnvironmentMap(Mode.CaseSensitive, pathSeparator, null); + } + + /** + * Returns a new empty {@link EnvironmentMap} with a specified {@code pathSeparator} + * to be used when dealing with PATH-like variables and that uses a + * case-preserving algorithm for comparing variable names. + * + * @param pathSeparator a char to be used as a separator when dealing with + * PATH-like variables. + * @return new empty case-preserving {@link EnvironmentMap}. + */ + public static EnvironmentMap createCasePreserving(char pathSeparator) { + Collator collator = Collator.getInstance(Locale.US); + collator.setStrength(Collator.PRIMARY); + return new EnvironmentMap(Mode.CasePreserving, pathSeparator, collator); + } + + /** + * Prepends provided path to the value of the specified variable. + * + *

As a result a variable with the name {@code name} will contain {@code path} + * at the first place. Also if the same path was already in the list of + * paths, that 'old' entry will be removed to avoid unneeded + * duplications.

+ * + *

If specified variable was not defined yet before this method + * invocation, it will have the only entry - this new path.

+ * + * @param name the name of the variable to change. + * @param path the path element to add to the list of path elements. Could + * be {@code null}, but in this case method does nothing. + */ + public void prependPath(String name, String path) { + if (path == null) { + return; + } + + String oldpath = get(name); + String newPath = path + (oldpath == null ? "" : (pathSeparator) + oldpath); // NOI18N + put(name, reducePath(newPath)); + } + + /** + * Appends provided path to the value of the specified variable. + * + *

As a result a {@code path} will be appended to a value of variable + * with the name {@code name}. If the same path was already in the list of + * paths the value of the variable will remain unchanged.

+ * + *

If the variable named {@code name} was not defined yet before this + * method invocation, the variable will have the only entry - this new + * path.

+ * + * @param name the name of the variable to change. + * @param path the path element to append to the list of path elements. + * Could be {@code null}, but in this case method does nothing. + */ + public void appendPath(String name, String path) { + if (path == null) { + return; + } + + String oldpath = get(name); + String newPath = (oldpath == null ? "" : oldpath + (pathSeparator)) + path; // NOI18N + put(name, reducePath(newPath)); + } + + private String reducePath(String path) { + if (path.isEmpty()) { + return path; + } + + LinkedHashSet elems = new LinkedHashSet(); + String e; + elems.addAll(Arrays.asList(path.split("" + pathSeparator))); // NOI18N + StringBuilder sb = new StringBuilder(); + for (String elem : elems) { + e = elem.trim(); + if (e.isEmpty()) { + continue; + } + sb.append(pathSeparator).append(e); + } + return sb.substring(1); + } + + /** + * Deep copy of the map. + * + * @return a copy of this map with the same characteristics (path separator, + * case sensitivity). + * @throws CloneNotSupportedException + */ + @Override + public EnvironmentMap clone() throws CloneNotSupportedException { + MacroMap map = super.clone(); + return new EnvironmentMap(mode, map, pathSeparator); + } + + private enum Mode { + + CaseSensitive, + CasePreserving + } + + // + private static class EnvironmentMapAccessorImpl extends EnvironmentMapAccessor { + + @Override + public void store(EnvironmentMap envMap, Preferences envprop) throws BackingStoreException { + try { + EnvironmentMap copy = envMap.clone(); + envprop.put("*pathSeparator", "" + copy.pathSeparator); + envprop.put("*mode", "" + copy.mode.name()); + for (Map.Entry entry : copy.entrySet()) { + envprop.put(entry.getKey(), entry.getValue()); + } + envprop.flush(); + } catch (CloneNotSupportedException ex) { + Exceptions.printStackTrace(ex); + } + } + + @Override + public EnvironmentMap load(Preferences envprop) throws BackingStoreException { + List keys = new ArrayList(Arrays.asList(envprop.keys())); + keys.remove("*pathSeparator"); // NOI18N + keys.remove("*mode"); // NOI18N + String sps = envprop.get("*pathSeparator", ":"); // NOI18N + String smode = envprop.get("*mode", Mode.CaseSensitive.name()); // NOI18N + + char ps = sps.charAt(0); + Mode mode = Mode.valueOf(smode); + EnvironmentMap map = Mode.CaseSensitive.equals(mode) ? createCaseSensitive(ps) : createCasePreserving(ps); + for (String key : keys) { + map.put(key, envprop.get(key, "")); // NOI18N + } + return map; + } + } + + static { + EnvironmentMapAccessor.setDefault(new EnvironmentMapAccessorImpl()); + } + // +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/NativeExecutionException.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/NativeExecutionException.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/NativeExecutionException.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +/** + * The root exception of all execution-related exceptions. + * + * This exception is thrown if any execution problem occurs. + * + * @author akrasny + */ +public class NativeExecutionException extends Exception { + + static final long serialVersionUID = 1L; + + /** + * Constructs a NativeExecutionException with the specified, detailed + * message. + * + * @param msg the detail message. + */ + public NativeExecutionException(String msg) { + super(msg); + } + + /** + * Constructs a NativeExecutionException with the specified detail message + * and nested exception. + * + * @param msg the detail message. + * @param cause the nested exception. + */ + public NativeExecutionException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * Constructs a NativeExecutionException with the specified nested + * exception. + * + * @param cause the nested exception. + */ + public NativeExecutionException(Throwable cause) { + super(cause.getMessage(), cause); + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/NativeProcess.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/NativeProcess.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/NativeProcess.java @@ -0,0 +1,340 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api.process; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; +import org.netbeans.modules.nativeexecution.access.NativeProcessAccessor; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.api.process.ProcessStateChangeEvent.State; +import org.netbeans.modules.nativeexecution.common.ProcessStateChangeNotifier; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; +import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; + +/** + * An object that represents an external process. + * + * @author akrasny + */ +public final class NativeProcess extends Process { + + private static final RequestProcessor waitThreadPool = new RequestProcessor("Process waitThreadPool", 50); // NOI18N + private final ReentrantLock stateLock = new ReentrantLock(); + private final ProcessStateChangeNotifier notifier; + private final NativeProcessCreator creator; + private final NativeProcessParams params; + private final AtomicBoolean killed = new AtomicBoolean(false); + private State state = State.INITIAL; + private NativeProcessImplementation processImpl; + private Future waitTask = null; + private InputStream inputStream; + private InputStream errorStream; + private OutputStream outputStream; + + static { + NativeProcessAccessor.setDefault(new NativeProcessAccessorImpl()); + } + + NativeProcess(NativeProcessCreator creator, NativeProcessParams params, ProcessStateChangeNotifier notifier) { + this.creator = creator; + this.params = params; + this.notifier = notifier; + inputStream = new ByteArrayInputStream(new byte[0]); + errorStream = new ByteArrayInputStream(new byte[0]); + outputStream = new ByteArrayOutputStream(); + } + + NativeProcess start() throws NativeExecutionException { + try { + setState(State.STARTING); + processImpl = creator.createAndStart(params); + + inputStream = processImpl.getInputStream(); + errorStream = processImpl.getErrorStream(); + outputStream = processImpl.getOutputStream(); + + setState(State.RUNNING); + + waitTask = waitThreadPool.submit(new Callable() { + + @Override + public Integer call() throws Exception { + Thread.currentThread().setName("Waiting for " + processImpl.toString()); // NOI18N + int exitCode = -1; + + try { + exitCode = processImpl.waitResult(); + setState(State.FINISHED); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + setState(State.CANCELLED); + killProcess(); + return exitCode; + } catch (Exception ex) { + setState(State.ERROR); + killProcess(); + throw ex; + } catch (Throwable th) { + setState(State.ERROR); + killProcess(); + throw new java.util.concurrent.ExecutionException(th); + } + + return exitCode; + } + }); + } catch (Throwable ex) { + throw new NativeExecutionException(ex); + } + + return this; + } + + private void killProcess() { + if (killed.getAndSet(true)) { + return; + } + + processImpl.destroy(); + } + + /** + * Returns a current state of the process. + * + * @return the current state. + */ + public State getState() { + State result; + stateLock.lock(); + try { + result = state; + } finally { + stateLock.unlock(); + } + return result; + } + + private boolean isFinalState(final State state) { + return state == State.CANCELLED || state == State.ERROR || state == State.FINISHED; + } + + /** + * Sets a current state of the process. + * + * There is a notion of a final state. Once a process was set into one of + * the final state, it's state will not change. + * + * A final state is one of the CANCELLED, ERROR or FINISHED. + * + * @param state a new state of the process. + */ + private void setState(final State state) { + stateLock.lock(); + try { + if (this.state == state || isFinalState(this.state)) { + return; + } + + this.state = state; + + if (notifier != null) { + notifier.notifyProcessStateChange(state); + } + } finally { + stateLock.unlock(); + } + } + + /** + * Returns a PID of the process. + * + * PID is provided by implementors of an SPI. By convention, if PID cannot + * be provided, -1 is returned. + * + * @return the PID of the process or -1 if real PID cannot be provided. + */ + public int getPID() { + return processImpl.getPID(); + } + + /** + * Gets the output stream of the process. + * + *

Output to the stream is piped into the standard input stream of the + * process represented by this {@link NativeProcess} object.

+ * + * @return the output stream connected to the normal input of the process. + */ + @Override + public OutputStream getOutputStream() { + return outputStream; + } + + /** + * Gets the input stream of the process. + * + *

The stream obtains data piped from the standard output stream of the + * process represented by this {@link NativeProcess} object.

+ * + * @return the input stream connected to the normal output of the process. + */ + @Override + public InputStream getInputStream() { + return inputStream; + } + + /** + * Gets the error stream of the process. + * + *

The stream obtains data piped from the error output stream of the + * process represented by this {@link NativeProcess} object.

+ * + * @return the input stream connected to the error stream of the process. + */ + @Override + public InputStream getErrorStream() { + return errorStream; + } + + /** + * Causes the current thread to wait, if necessary, until the process + * represented by this {@link NativeProcess} object has terminated. + * + *

This method returns immediately if the process has already terminated. + * If the process has not yet terminated, the calling thread will be blocked + * until the process exits.

+ * + * @return the exit value of the process. By convention, 0 indicates normal + * termination. + * @throws InterruptedException if the current thread is interrupted by + * another thread while it is waiting, then the wait is ended and an + * InterruptedException is thrown. + */ + @Override + public int waitFor() throws InterruptedException { + try { + return waitTask.get(); + } catch (java.util.concurrent.ExecutionException ex) { + // Should not happen. + // Exception here could mean that there are problems in + // implementation + Exceptions.printStackTrace(ex); + throw new InternalError(ex.toString()); + } + } + + /** + * Returns the exit value for the process. + * + *

{@link IllegalThreadStateException} is thrown if process is not + * terminated yet.

+ * + * @return the exit value of the process represented by this {@link NativeProcess} + * object. By convention, the value {@code 0} indicates normal termination. + */ + @Override + public int exitValue() { + if (!waitTask.isDone()) { + // Process not started/finished yet... + throw new IllegalThreadStateException(); + } + + try { + return waitTask.get(); + } catch (InterruptedException ex) { + // Should not happen + Thread.interrupted(); + return -1; + } catch (java.util.concurrent.ExecutionException ex) { + // Should not happen. + // Exception here could mean that there are problems in + // implementation + Exceptions.printStackTrace(ex); + throw new InternalError(ex.toString()); + } + } + + /** + * Kills the subprocess. + * + * The subprocess represented by this {@link NativeProcess} object is + * forcibly terminated. + */ + @Override + public void destroy() { + waitTask.cancel(true); + } + + /** + * Returns a human-readable identification of the + * {@link NativeProcess}. + * + * @return string that identifies the {@link NativeProcess}. + */ + @Override + public String toString() { + return "/PID=" + getPID() + "/ " + processImpl.toString(); // NOI18N + } + + private static class NativeProcessAccessorImpl extends NativeProcessAccessor { + + @Override + public Connection getConnection(NativeProcess process) { + return process.params.getConnection(); + } + + @Override + public NativeProcessParams getProcessParams(NativeProcess process) { + return process.params; + } + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/NativeProcessBuilder.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/NativeProcessBuilder.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/NativeProcessBuilder.java @@ -0,0 +1,368 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api.process; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import org.netbeans.modules.nativeexecution.access.NativeProcessBuilderFactory; +import org.netbeans.modules.nativeexecution.access.NativeProcessParamsFactory; +import org.netbeans.modules.nativeexecution.api.BrokenConnectionException; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.api.process.ProcessStateChangeEvent.State; +import org.netbeans.modules.nativeexecution.common.ConnectionsInfo; +import org.netbeans.modules.nativeexecution.common.ProcessStateChangeNotifier; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfo; +import org.openide.util.RequestProcessor; + +/** + * This class is used to create an external processes. + * + *

Each NativeProcessBuilder instance manages a collection of process + * attributes. The {@link #call()} method creates a new {@link NativeProcess} + * instance with those attributes. The {@link #call()} method can be invoked + * repeatedly from the same instance to create new processes with identical or + * related attributes.

+ * + *

There are several common attributes that could be configured directly by + * invoking appropriate methods. Additional {@link Connection}-specific + * attributes could be provided by a specific implementation. Access to those + * attributes is done using {@link #getSupportedProperties()} and {@link #setProperty(String, Object)} + * methods.

+ * + *

Modifying a NativeProcessBuilder's attributes will affect processes + * subsequently started by it's {@link #call()} method, but will never affect + * previously started processes.

+ * + *

This class is NOT thread-safe.

+ * + * @see ConnectionManager + * @author akrasny + */ +public final class NativeProcessBuilder implements Callable { + + private final NativeProcessCreator creator; + private final Connection connection; + private final Map> supportedProperties; + private final Map properties = new HashMap(); + private final List listeners = new ArrayList(); + private final EnvironmentMap environmentMap; + private List command; + private boolean redirectError; + private String shellScript; + private String shell; + private String wdir; + + /** + * Creates a new instance of NativeProcessBuilder. + * + * @param connection the {@link Connection} to use for this builder. + * @param supportedProperties a map of supported configurable properties. + */ + private NativeProcessBuilder(NativeProcessCreator creator, Connection connection) { + Map> propDescr = creator.getSupportedProperties(); + ConnectionInfo info = ConnectionsInfo.getConnectionInfo(connection.getURI()); + this.creator = creator; + this.connection = connection; + this.supportedProperties = propDescr == null + ? Collections.>emptyMap() + : Collections.unmodifiableMap(propDescr); + EnvironmentMap envMapCopy = null; + try { + envMapCopy = (info == null || info.getEnvironmentMap() == null) + ? EnvironmentMap.createCaseSensitive(':') + : info.getEnvironmentMap().clone(); + } catch (CloneNotSupportedException ex) { + } finally { + environmentMap = envMapCopy == null ? EnvironmentMap.createCaseSensitive(':') : envMapCopy; + } + } + + /** + * Adds a listener to the list that is notified each time a state of any {@link NativeProcess} + * created by this builder changes. + * + *

Added listener will be passed to every subsequently created + * NativeProcess. Already running NativeProcess will not see any changes in + * the listeners list.

+ * + * @param listener the {@link ProcessStateChangeListener} to add. + */ + public NativeProcessBuilder addProcessListener(ProcessStateChangeListener listener) { + listeners.add(listener); + return this; + } + + /** + * Sets the Command attribute. + * + *

Sets an executable and arguments of the process to start. This method + * does a copy of the command list. No any validation is performed.

+ * + *

Note that setting a command with this method erases any data + * configured with the {@link #setShellScript(String, String)} method.

+ * + * + * @param executable the executable to start. + * @param arguments the list of arguments that will be passed to the + * executable. + * @return This process builder. + * @see #setShellScript(String, String) + */ + public NativeProcessBuilder setCommand(String executable, String... arguments) { + command = new ArrayList(arguments.length + 1); + command.add(executable); + command.addAll(Arrays.asList(arguments)); + shell = null; + shellScript = null; + return this; + } + + /** + * Sets the ShellScript attribute. + * + * + *

Defines a shell script (text) that should be interpreted by the + * specified shell.

+ * + *

In case of using {@code /bin/sh}, that would be the same as calling + * {@code setCommand("/bin/sh", "-s")} and writing a script to it's input + * stream.

+ * + *

Note that setting a command with this method erases any data + * configured with the {@link #setCommand(String, String[])} method.

+ * + * @param shell the shell to use. If {@code null}, then default user's shell + * is used. + * @param script the script to be passed to the shell. + * @return This process builder. + * @see #setShellScript(String, String) + */ + public NativeProcessBuilder setShellScript(String shell, String script) { + this.shell = shell; + this.shellScript = script; + command = null; + return this; + } + + /** + * Configures a working directory attribute. + * + *

Process subsequently created by the call() method on this builder + * will be executed with this directory as a current working dir.

+ * + *

The default value is undefined.

+ * + * @param workingDirectory working directory to start a process in. + * @return This process builder. + */ + public NativeProcessBuilder setWorkingDirectory(String workingDirectory) { + this.wdir = workingDirectory; + return this; + } + + /** + * Configures a error stream redirection attribute. + * + *

If this property is true, then any error output generated by processes + * subsequently started by this builder will be merged with the standard + * output, so that both can be read using a stream returned by + * {@code NativeProcess.getInputStream()} method.

+ * + *

The initial value is false.

+ * + * @param redirectErrorStream the new value of the attribute. + * @return This process builder. + */ + public NativeProcessBuilder redirectErrorStream(boolean redirectErrorStream) { + this.redirectError = redirectErrorStream; + return this; + } + + /** + * Returns a string map view of this process builder's environment. + * + *

Whenever a process builder is created, the environment is initialized + * to a copy of the {@link Connection}'s environment (if applicable). + * Processes subsequently started by this builder will use this map as their + * environment.

+ * + * @return This process builder's environment. + */ + public EnvironmentMap getEnvironmentMap() { + return environmentMap; + } + + /** + * Returns unmodifiable map of all extra properties configurable with this + * builder. + * + * @return unmodifiable map of all configurable properties. + */ + public Map> getSupportedProperties() { + return supportedProperties; + } + + /** + * Setter for additional builder implementation specific properties. + * + *

IllegalArgumentException will be thrown if builder doesn't know about + * the property or if passed value has an unexpected type.

+ * + * + * @param propertyName the name of a property to set. + * @param value the value of a property. + * @return This process builder. + * @see #getSupportedProperties() + */ + public NativeProcessBuilder setProperty(String propertyName, Object value) { + if (!supportedProperties.containsKey(propertyName)) { + throw new IllegalArgumentException("Unsupported property: " + propertyName); // NOI18N + } + + if (value == null) { + properties.remove(propertyName); + return this; + } + + Class clazz = supportedProperties.get(propertyName); + + if (!clazz.isAssignableFrom(value.getClass())) { + throw new IllegalArgumentException("Wrong property type: " + clazz.getName() + " expected for " + propertyName + " but " + value.getClass() + " was provided"); // NOI18N + } + + properties.put(propertyName, value); + return this; + } + + /** + * Starts a new process using the attributes of this process builder. + * + * @return a new {@link NativeProcess}. + * @throws NativeExecutionException if process cannot be created. + */ + @Override + public NativeProcess call() throws NativeExecutionException { + if (!connection.isConnected()) { + throw new BrokenConnectionException(connection.getURI()); + } + + final AtomicReference pref = new AtomicReference(); + final ReentrantLock lock = new ReentrantLock(); + + ProcessStateChangeNotifier notifyer = null; + + if (!listeners.isEmpty()) { + final List ll = new ArrayList(listeners); + final RequestProcessor notificationThread = new RequestProcessor(); + notifyer = new ProcessStateChangeNotifier() { + + @Override + public void notifyProcessStateChange(final State newProcessState) { + notificationThread.post(new Runnable() { + + @Override + public void run() { + lock.lock(); + try { + ProcessStateChangeEvent evt = new ProcessStateChangeEvent(pref.get(), newProcessState); + for (ProcessStateChangeListener l : ll) { + l.processStateChanged(evt); + } + } finally { + lock.unlock(); + } + } + }); + } + }; + } + + ConnectionInfo connectionInfo = ConnectionsInfo.getConnectionInfo(connection.getURI()); + + NativeProcessParams params = NativeProcessParamsFactory.getDefault().newNativeProcessParams( + connection, + connectionInfo == null ? null : connectionInfo.getProperties(), + environmentMap.toMap(), + new HashMap(properties), + command, + shell, + shellScript, + redirectError, + wdir); + + lock.lock(); + try { + + NativeProcess result = new NativeProcess(creator, params, notifyer); + pref.set(result); + return result.start(); + } finally { + lock.unlock(); + } + } + + // + static { + NativeProcessBuilderFactory.setDefault(new NativeProcessBuilderFactoryImpl()); + } + + private static class NativeProcessBuilderFactoryImpl extends NativeProcessBuilderFactory { + + @Override + public NativeProcessBuilder newProcessBuilder(NativeProcessCreator impl, Connection connection) { + return new NativeProcessBuilder(impl, connection); + } + } + // +} \ No newline at end of file diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/ProcessStateChangeEvent.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/ProcessStateChangeEvent.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/ProcessStateChangeEvent.java @@ -0,0 +1,142 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api.process; + +import java.util.EventObject; + +/** + * ProcessStateChangeEvent is used to notify interested parties that state of + * the source's process has changed. + * + * @author akrasny + */ +public final class ProcessStateChangeEvent extends EventObject { + + private static final long serialVersionUID = 1L; + private final State state; + private final long ts; + + /** + * Constructs a new ProcessStateChangeEvent event. + * + * @param process the process that originated the event. + * @param state the new state of the process. + */ + public ProcessStateChangeEvent(NativeProcess process, State state) { + this(process, state, System.nanoTime()); + } + + /** + * Constructs a new ProcessStateChangeEvent event. + * + * @param process the process that originated the event. + * @param state the new state of the process. + * @param nanoTime the exact time of the event. + */ + public ProcessStateChangeEvent(NativeProcess process, State state, long nanoTime) { + super(process); + this.state = state; + this.ts = nanoTime; + } + + /** + * Returns a state of the process on the moment of this event creation. + * + * @return the state of the process on the moment of this event creation. + */ + public State getState() { + return state; + } + + /** + * Returns the source {@link NativeProcess} for this event. + * + * @return the event source + */ + @Override + public NativeProcess getSource() { + return (NativeProcess) super.getSource(); + } + + /** + * Returns the timestamp of when this event occurred. + * + * @return this event's timestamp. + */ + public long getEventNanoTime() { + return ts; + } + + /** + * Enumerates all possible states of a {@link NativeProcess}. + */ + public enum State { + + /** + * Native process is in an Initial state. This means that it has not + * been started yet. + */ + INITIAL, + /** + * Native process is starting. This means that it has been submitted, + * but no PID is received so far. + */ + STARTING, + /** + * Native process runs. This means that process successfully started and + * it's PID is already known. + */ + RUNNING, + /** + * Native process exited. + */ + FINISHED, + /** + * Native process submission failed due to some exception. + */ + ERROR, + /** + * Native process forcibly terminated. + */ + CANCELLED + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/ProcessStateChangeListener.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/ProcessStateChangeListener.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/api/process/ProcessStateChangeListener.java @@ -0,0 +1,79 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api.process; + +import java.util.EventListener; + +/** + * A listener that, added passed to a NativeProcessBuilders will be notified on + * changes in a state of NativeProcesses created by those builder. + * + * @author akrasny + */ +public interface ProcessStateChangeListener extends EventListener { + + /** + * Is invoked once process has changed it's state. + * + *
+     * Process has determinated order of states it can be set to:
+     * INITIAL ---> STARTING ---> RUNNING  ---> FINISHED
+     *          |-> CANCELLED |-> CENCELLED |-> CANCELLED
+     *          |-> ERROR     |-> ERROR     |-> ERROR
+     *
+     * CANCELLED, ERROR and FINISHED are terminal states.
+     * 
+ * + *

It is guaranteed that:

  • listeners are notified in the same + * (per process) not EDT thread;
  • listener receives events in the + * order they occur;
  • once process reaches it's final (terminal) + * state, no further events will be generated for that process.
+ *

+ * + *

Note: listeners are notified asynchronously relative a process + * itself. This means that at a time of event delivery the real state of a + * process may already differ.

+ * + * @param evt the event this notification is about + */ + public void processStateChanged(ProcessStateChangeEvent evt); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/Bundle.properties b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/Bundle.properties new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/Bundle.properties @@ -0,0 +1,43 @@ +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2012 Oracle and/or its affiliates. All rights reserved. +# +# Oracle and Java are registered trademarks of Oracle and/or its affiliates. +# Other names may be trademarks of their respective owners. +# +# The contents of this file are subject to the terms of either the GNU +# General Public License Version 2 only ("GPL") or the Common +# Development and Distribution License("CDDL") (collectively, the +# "License"). You may not use this file except in compliance with the +# License. You can obtain a copy of the License at +# http://www.netbeans.org/cddl-gplv2.html +# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the +# specific language governing permissions and limitations under the +# License. When distributing the software, include this License Header +# Notice in each file and include the License file at +# nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the GPL Version 2 section of the License file that +# accompanied this code. If applicable, add the following below the +# License Header, with the fields enclosed by brackets [] replaced by +# your own identifying information: +# "Portions Copyrighted [year] [name of copyright owner]" +# +# If you wish your version of this file to be governed by only the CDDL +# or only the GPL Version 2, indicate your decision by adding +# "[Contributor] elects to include this software in this distribution +# under the [CDDL or GPL Version 2] license." If you do not indicate a +# single choice of license, a recipient has the option to distribute +# your version of this file under either the CDDL, the GPL Version 2 or +# to extend the choice of license to its licensees as provided above. +# However, if you add GPL Version 2 code and therefore, elected the GPL +# Version 2 license, then the option applies only if the new code is +# made subject to such option by the copyright holder. +# +# Contributor(s): +# +# Portions Copyrighted 2012 Sun Microsystems, Inc. + +Connector.progress.message=Connection progress +Connector.progress.connectTo=Connecting to {0} +Connector.progress.fetchingInfo=Getting connection info diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionListeners.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionListeners.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionListeners.java @@ -0,0 +1,204 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.ConnectionStateChangeEvent; +import org.netbeans.modules.nativeexecution.api.ConnectionStateChangeEvent.State; +import org.netbeans.modules.nativeexecution.api.ConnectionStateChangeListener; +import org.netbeans.modules.nativeexecution.util.URIMatcher; +import org.openide.util.RequestProcessor; +import org.openide.util.RequestProcessor.Task; +import org.openide.util.WeakSet; + +/** + * + * @author akrasny + */ +public final class ConnectionListeners { + + private static final RequestProcessor RP = new RequestProcessor("ConnectionListeners", 1); // NOI18N + private static final Map> map = + new HashMap>(); + // always accessed from the single-thread RP + private static final List active = new ArrayList(); + private static final Task ttask = RP.create(new TrackTask(), true); + + private ConnectionListeners() { + } + + public static void addConnectionStateChangeListener(ConnectionStateChangeListener listener, URI uri) { + URIMatcher m = new URIMatcher(uri); + + synchronized (map) { + for (Map.Entry> entry : map.entrySet()) { + if (m.isIdenticalURI(entry.getKey())) { + entry.getValue().add(listener); + return; + } + } + + WeakSet set = new WeakSet(); + set.add(listener); + map.put(uri, new WeakSet(set)); + } + + ttask.schedule(0); + } + + public static void removeConnectionStateChangeListener(ConnectionStateChangeListener listener, URI uri) { + URIMatcher m = new URIMatcher(uri); + + synchronized (map) { + for (Map.Entry> entry : map.entrySet()) { + if (m.isIdenticalURI(entry.getKey())) { + entry.getValue().remove(listener); + return; + } + } + } + } + + public static void notifyListeners(URI uri, ConnectionStateChangeEvent ev) { + RP.post(new NotificationTask(uri, ev)).waitFinished(); + } + + // When disconnect is called, first connection get closed and then listeners + // are notified. If at the same time tracking task is running, it may detect + // that connection is closed before it gets removed from the list of active + // connections and hence decide that connection was lost and notify + // listeners with CONNECTION_LOST event (which is wrong in this situation). + // To avoid this - just stop the TrackingTask before doing real disconnect. + // Called from ConnectionManager. + // + public static void stopTrackingTask() { + ttask.cancel(); + ttask.waitFinished(); + } + + private static class NotificationTask implements Runnable { + + private final URI uri; + private final ConnectionStateChangeEvent ev; + + public NotificationTask(URI uri, ConnectionStateChangeEvent ev) { + this.uri = uri; + this.ev = ev; + } + + @Override + public void run() { + URIMatcher m = new URIMatcher(uri); + List toNotify = new ArrayList(); + + synchronized (map) { + for (Map.Entry> entry : map.entrySet()) { + if (m.isIdenticalURI(entry.getKey())) { + WeakSet set = entry.getValue(); + for (ConnectionStateChangeListener l : set) { + toNotify.add(l); + } + } + } + } + + for (ConnectionStateChangeListener l : toNotify) { + l.connectionStateChanged(ev); + } + + boolean activated = State.CONNECTION_ESTABLISHED.equals(ev.getState()); + + Iterator it = active.iterator(); + while (it.hasNext()) { + if (m.isIdenticalURI(it.next())) { + assert !activated; + it.remove(); + } + } + + if (activated) { + active.add(uri); + } + + ttask.schedule(0); + } + } + + /** + * This task periodically asks registered active connections for their state + * and detects lost connections... + */ + private static class TrackTask implements Runnable { + + @Override + public void run() { + List lost = new ArrayList(); + + boolean restart = false; + + for (URI uri : active) { + Connection c = ConnectionManager.getConnection(uri); + if (c != null && c.isConnected()) { + restart = true; + continue; + } + + lost.add(uri); + } + + for (URI uri : lost) { + notifyListeners(uri, new ConnectionStateChangeEvent(uri, State.CONNECTION_LOST)); + } + + if (restart) { + ttask.schedule(1000); + } + } + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionServiceLookup.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionServiceLookup.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionServiceLookup.java @@ -0,0 +1,74 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import java.net.URI; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author akrasny + */ +public final class ConnectionServiceLookup { + + private ConnectionServiceLookup() { + } + + public static Lookup getServiceLookup(URI uri) { + String scheme = uri.getScheme(); + if (scheme == null) { + String src = uri.toString(); + StringBuilder sb = new StringBuilder(); + for (char c : src.toCharArray()) { + if (Character.isLetterOrDigit(c)) { + sb.append(c); + } else { + break; + } + } + scheme = sb.toString(); + } + Lookup lookup = Lookups.forPath("ConnectionService/" + scheme); // NOI18N + return lookup; + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsInfo.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsInfo.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsInfo.java @@ -0,0 +1,150 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import java.net.URI; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.modules.nativeexecution.access.ConnectionAccessor; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfo; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfoProvider; +import org.netbeans.modules.nativeexecution.util.URIMatcher; +import org.openide.util.Lookup; + +/** + * An entry point for getting {@link Connection} information from providers. + * + * @author akrasny + */ +public final class ConnectionsInfo { + + private static final Map infoCache = new HashMap(); + + private ConnectionsInfo() { + } + + public static ConnectionInfo getConnectionInfo(URI uri) { + synchronized (infoCache) { + URIMatcher m = new URIMatcher(uri); + for (Map.Entry entry : infoCache.entrySet()) { + if (m.isIdenticalURI(entry.getKey())) { + return entry.getValue(); + } + } + } + + return null; + } + + public static void setConnectionInfo(URI uri, ConnectionInfo info) { + synchronized (infoCache) { + URIMatcher m = new URIMatcher(uri); + for (Map.Entry entry : infoCache.entrySet()) { + if (m.isIdenticalURI(entry.getKey())) { + entry.setValue(info); + return; + } + } + infoCache.put(uri, info); + } + } + + public static void refreshConnectionInfo(final Connection connection) { + final ConnectionAccessor access = ConnectionAccessor.getDefault(); + final Lookup lookup = access.getConnectionLookup(connection); + + if (lookup == null) { + return; + } + + final Collection providers = + lookup.lookupAll(ConnectionInfoProvider.class); + + final ProxyInfo info = new ProxyInfo(); + + for (ConnectionInfoProvider provider : providers) { + try { + ConnectionInfo data = provider.getConnectionInfo(connection); + if (data != null) { + EnvironmentMap map = data.getEnvironmentMap(); + + if (info.map == null) { + try { + info.map = map.clone(); + } catch (CloneNotSupportedException ex) { + // Exceptions.printStackTrace(ex); + } + } else { + for (Map.Entry entry : map.entrySet()) { + info.map.put(entry.getKey(), entry.getValue()); + } + } + + info.properties.putAll(data.getProperties()); + } + } catch (NativeExecutionException ex) { + } + } + + setConnectionInfo(connection.getURI(), info); + } + + private static final class ProxyInfo implements ConnectionInfo { + + final Map properties = new HashMap(); + EnvironmentMap map = null; + + @Override + public EnvironmentMap getEnvironmentMap() { + return map; + } + + @Override + public Map getProperties() { + return properties; + } + } +} \ No newline at end of file diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsPool.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsPool.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsPool.java @@ -0,0 +1,85 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.util.URIMatcher; +import org.openide.util.WeakSet; + +/** + * In-memory pool of all ever-known at least once established connections. + * + * @author Andrew + */ +public final class ConnectionsPool { + + private static final WeakSet pool = new WeakSet(); + + private ConnectionsPool() { + } + + public static void add(Connection connection) { + URIMatcher m = new URIMatcher(connection.getURI()); + + synchronized (pool) { + for (Connection c : pool) { + if (m.isIdenticalURI(c.getURI())) { + return; + } + } + pool.add(connection); + } + } + + public static Connection get(URI uri) { + URIMatcher m = new URIMatcher(uri); + synchronized (pool) { + for (Connection c : pool) { + if (m.isIdenticalURI(c.getURI())) { + return c; + } + } + } + return null; + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsRegistry.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsRegistry.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ConnectionsRegistry.java @@ -0,0 +1,302 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import java.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import javax.swing.event.ChangeListener; +import org.netbeans.modules.nativeexecution.access.ConnectionAccessor; +import org.netbeans.modules.nativeexecution.access.EnvironmentMapAccessor; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfo; +import org.netbeans.modules.nativeexecution.util.URIMatcher; +import org.openide.util.ChangeSupport; +import org.openide.util.Exceptions; +import org.openide.util.NbPreferences; +import org.openide.util.RequestProcessor; +import org.openide.util.RequestProcessor.Task; + +/** + * + * @author akrasny + */ +public final class ConnectionsRegistry { +// +// private static final RequestProcessor RP = new RequestProcessor(ConnectionsRegistry.class.getName(), 1); +// private static final ConnectionsRegistry instance = new ConnectionsRegistry(); +// private final AtomicReference restoreTask = new AtomicReference(); +// private final CopyOnWriteArraySet connections = new CopyOnWriteArraySet(); +// private final ChangeSupport cs = new ChangeSupport(this); +// private final Task writeTask; +// +// static { +// instance.init(); +// } +// +// private ConnectionsRegistry() { +// writeTask = RP.create(new Runnable() { +// +// @Override +// public void run() { +// storeImpl(); +// } +// }, true); +// } +// +// private void init() { +// restoreTask.set(RP.post(new Runnable() { +// +// @Override +// public void run() { +// connections.addAll(restoreImpl()); +// } +// })); +// } +// +// public static ConnectionsRegistry getInstance() { +// Task r = instance.restoreTask.get(); +// if (r != null) { +// r.waitFinished(); +// instance.restoreTask.set(null); +// } +// return instance; +// } +// +// public Connection getConnection(URI uri) { +// URIMatcher matcher = new URIMatcher(uri); +// for (Connection connection : connections) { +// if (matcher.isIdenticalURI(connection.getURI())) { +// return connection; +// } +// } +// return null; +// } +// +// public List getConnections() { +// return Arrays.asList(connections.toArray(new Connection[connections.size()])); +// } +// +// public void addChangeListener(ChangeListener listener) { +// cs.addChangeListener(listener); +// } +// +// public void removeChangeListener(ChangeListener listener) { +// cs.addChangeListener(listener); +// } +// +// public void addConnection(Connection connection) { +// connections.add(connection); +// changed(); +// } +// +// public void removeConnection(Connection connection) { +// connections.remove(connection); +// Connections.setConnectionInfo(connection.getURI(), null); +// changed(); +// } +// +// private void changed() { +// writeTask.schedule(0); +// cs.fireChange(); +// } +// +// private void storeImpl() { +// List data = getConnections(); +// Preferences props = NbPreferences.forModule(ConnectionsRegistry.class); +// try { +// props.clear(); +// +// for (Connection connection : data) { +// final URI uri = connection.getURI(); +// Preferences cprop = props.node(toFileName(uri)); +// ConnectionInfo info = Connections.getConnectionInfo(uri); +// if (info != null) { +// EnvironmentMap envMap = info.getEnvironmentMap(); +// if (envMap != null) { +// Preferences envprop = cprop.node("env"); // NOI18N +// EnvironmentMapAccessor.getDefault().store(envMap, envprop); +// envprop.flush(); +// } +// +// Map connprop = info.getProperties(); +// if (connprop != null) { +// Preferences infoprop = cprop.node("props"); // NOI18N +// for (Map.Entry entry : connprop.entrySet()) { +// infoprop.put(entry.getKey(), entry.getValue()); +// } +// infoprop.flush(); +// } +// } +// cprop.flush(); +// } +// +// props.flush(); +// } catch (BackingStoreException ex) { +// Exceptions.printStackTrace(ex); +// } +// } +// +// private static List restoreImpl() { +// ConnectionAccessor access = ConnectionAccessor.getDefault(); +// Preferences props = NbPreferences.forModule(ConnectionsRegistry.class); +// List result = new LinkedList(); +// try { +// for (String fname : props.childrenNames()) { +// URI uri = fromFileName(fname); +// if (uri == null) { +// continue; +// } +// +// Connection connection = access.createConnection(uri); +// Preferences cprops = props.node(fname); +// Preferences envprop = cprops.node("env"); // NOI18N +// final EnvironmentMap envmap = EnvironmentMapAccessor.getDefault().load(envprop); +// +// Preferences infoprop = cprops.node("props"); // NOI18N +// final Map connprop = new HashMap(); +// for (String key : infoprop.keys()) { +// connprop.put(key, infoprop.get(key, "")); // NOI18N +// } +// +// Connections.setConnectionInfo(uri, new ConnectionInfo() { +// +// @Override +// public EnvironmentMap getEnvironmentMap() { +// return envmap; +// } +// +// @Override +// public Map getProperties() { +// return connprop; +// } +// }); +// +// getInstance().addConnection(connection); +// } +// } catch (BackingStoreException ex) { +// Exceptions.printStackTrace(ex); +// } +// return result; +// } +// +// private static String toFileName(URI uri) { +// String id = uri.toString(); +// StringBuilder res = new StringBuilder(); +// for (char c : id.toCharArray()) { +// switch (c) { +// case '/': +// res.append("%s"); +// break; +// case ':': +// res.append("%c"); +// break; +// case '%': +// res.append("%%"); // NOI18N +// break; +// default: +// res.append(c); +// } +// } +// return res.toString(); +// } +// +// private static URI fromFileName(String envName) { +// StringBuilder res = new StringBuilder(); +// char[] chars = envName.toCharArray(); +// for (int i = 0; i < chars.length; i++) { +// char c = chars[i]; +// if (c == '%') { +// if (++i == chars.length) { +// return null; +// } +// switch (chars[i]) { +// case '%': +// res.append('%'); +// break; +// case 'c': +// res.append(':'); +// break; +// case 's': +// res.append('/'); +// break; +// } +// } else { +// res.append(c); +// } +// } +// +// try { +// return URI.create(res.toString()); +// } catch (IllegalArgumentException ex) { +// } +// +// return null; +// } +// +// void waitReady(final boolean shutdown) { +// try { +// RP.submit(new Runnable() { +// +// @Override +// public void run() { +// if (shutdown) { +// RP.shutdown(); +// } +// } +// }).get(); +// } catch (InterruptedException ex) { +// Exceptions.printStackTrace(ex); +// } catch (ExecutionException ex) { +// Exceptions.printStackTrace(ex); +// } +// } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/Connector.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/Connector.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/Connector.java @@ -0,0 +1,192 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import java.net.URI; +import java.util.concurrent.atomic.AtomicReference; +import javax.swing.SwingUtilities; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.netbeans.api.progress.ProgressRunnable; +import org.netbeans.api.progress.ProgressUtils; +import org.netbeans.modules.nativeexecution.access.ConnectionAccessor; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionCancelledException; +import org.netbeans.modules.nativeexecution.api.ConnectionException; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.ConnectorImplementation; +import org.openide.util.Cancellable; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; + +/** + * + * @author akrasny + */ +public final class Connector { + + public static ConnectionImplementation connect(URI uri, AuthDataProvider authDataProvider) throws NativeExecutionException { + return connectImpl(uri, authDataProvider); + } + + private static ConnectionImplementation connectImpl(final URI uri, final AuthDataProvider authDataProvider) throws NativeExecutionException { + final AtomicReference resultRef = new AtomicReference(); + final AtomicReference exRef = new AtomicReference(); + final String msg = NbBundle.getMessage(Connector.class, "Connector.progress.message"); // NOI18N + final ProgressRunnableImpl connectionTask = new ProgressRunnableImpl( + uri, authDataProvider, resultRef, exRef, + SwingUtilities.isEventDispatchThread() ? null : Thread.currentThread()); + + if (SwingUtilities.isEventDispatchThread()) { + ProgressUtils.showProgressDialogAndRun(connectionTask, msg, true); + } else { + ProgressHandle progressHandle = ProgressHandleFactory.createHandle(msg, connectionTask); + try { + progressHandle.start(); + progressHandle.switchToIndeterminate(); + connectionTask.run(progressHandle); + } finally { + progressHandle.finish(); + } + } + + NativeExecutionException exception = exRef.get(); + + if (exception != null) { + throw exception; + } + + return resultRef.get(); + } + + private static class ProxyAuthDataProvider implements AuthDataProvider { + + private final AuthDataProvider defaultProvider; + private final AuthDataProvider origProvider; + + private ProxyAuthDataProvider(Lookup lookup, AuthDataProvider authDataProvider) { + this.origProvider = authDataProvider; + this.defaultProvider = lookup.lookup(AuthDataProvider.class); + } + + @Override + public T getProperty(URI uri, Class clazz, String name, Object... misc) { + T result = origProvider == null ? null + : origProvider.getProperty(uri, clazz, name, misc); + if (result == null && defaultProvider != null) { + result = defaultProvider.getProperty(uri, clazz, name, misc); + } + return result; + } + } + + private static class ProgressRunnableImpl implements ProgressRunnable, Cancellable { + + private final URI uri; + private final AuthDataProvider authDataProvider; + private final AtomicReference resultRef; + private final AtomicReference exRef; + private final Thread thread; + + public ProgressRunnableImpl(URI uri, AuthDataProvider authDataProvider, + AtomicReference resultRef, AtomicReference exRef, + Thread thread) { + this.uri = uri; + this.authDataProvider = authDataProvider; + this.resultRef = resultRef; + this.exRef = exRef; + this.thread = thread; + } + + @Override + public Void run(final ProgressHandle handle) { + final ConnectionAccessor access = ConnectionAccessor.getDefault(); + final Lookup lookup = ConnectionServiceLookup.getServiceLookup(uri); + final AuthDataProvider provider = new ProxyAuthDataProvider(lookup, authDataProvider); + + for (ConnectorImplementation connector : lookup.lookupAll(ConnectorImplementation.class)) { + try { + ConnectionImplementation connectionImpl = connector.connectTo(uri, provider); + + if (connectionImpl == null) { + continue; + } + + // We create a new Connection, but without any registration + // in any registry... It's too early - need to get it's + // info first (But for this we need a Connection, as getting + // info may require starting some processes using this + // connection + Connection tmpConnection = access.createConnection(connectionImpl); + + handle.setDisplayName(NbBundle.getMessage(Connector.class, + "Connector.progress.fetchingInfo")); // NOI18N + + ConnectionsInfo.refreshConnectionInfo(tmpConnection); + resultRef.set(connectionImpl); + + // done + return null; + } catch (NativeExecutionException ex) { + exRef.compareAndSet(null, ex); + } catch (InterruptedException ex) { + exRef.compareAndSet(null, new ConnectionCancelledException(uri)); + break; + } + } + + exRef.compareAndSet(null, new NativeExecutionException("Unsupported connection scheme: " + uri)); // NOI18N + return null; + } + + @Override + public boolean cancel() { + exRef.compareAndSet(null, new ConnectionCancelledException(uri)); + if (thread != null) { + thread.interrupt(); + } + return true; + } + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ProcessStateChangeNotifier.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ProcessStateChangeNotifier.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/common/ProcessStateChangeNotifier.java @@ -0,0 +1,53 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import org.netbeans.modules.nativeexecution.api.process.ProcessStateChangeEvent.State; + +/** + * + * @author akrasny + */ +public interface ProcessStateChangeNotifier { + + public void notifyProcessStateChange(final State newProcessState); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/resources/Bundle.properties b/nativeexecution/src/org/netbeans/modules/nativeexecution/resources/Bundle.properties new file mode 100755 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/resources/Bundle.properties @@ -0,0 +1,2 @@ +OpenIDE-Module-Name=Native Execution API + diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/AuthDataProvider.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/AuthDataProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/AuthDataProvider.java @@ -0,0 +1,122 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.common.Connector; + +/** + * An interface to be implemented to provide an authorization/authentication + * data needed by a {@link ConnectorImplementation} to establish a {@link ConnectionImplementation}. + * + *

{@link AuthDataProvider} has a set of constants that represent commonly + * used identifiers for some authorization/authentication info.

+ * + *

The data that is required to establish a connection depends on a nature + * of the connection and features supported by a {@link ConnectorImplementation}. + * In a simple case this could be just a username and a password. In other cases + * this could be a location of some secret file, passphrase or any other + * data.

+ * + *

In some cases implementor could extract needed information from the + * destination URI. In other cases it could look into some database or ask user + * to provide some input.

+ * + *

Possible usage: + *

+ *     Connection c = ConnectionManager.connect(new URI("ssh://tester@testhost"), new AuthDataProvider {
+ *
+ *       public  T getProperty(URI uri, Class clazz, String name, Object... misc) {
+ *         Object result = null;
+ *
+ *         if (String.class.equals(clazz) && "KnownHostsFile".equals(name)) {
+ *           result = System.getProperty("user.home") + "/.ssh/known_hosts";
+ *         } else if (char[].class.equals(clazz) && AuthDataProvider.PASSWORD.equals(name)) {
+ *           result = new char[]{'t', 'e', 's', 't', 'e', 'r'};
+ *         }
+ *
+ *         return clazz.cast(result);
+ *       }
+ *     });
+ *
+ * 

+ * + * @author akrasny + */ +public interface AuthDataProvider { + + /** + * Common identifier used for requesting a username. + */ + public static final String USERNAME = "username"; // NOI18N + /** + * Common identifier used for requesting a user password. + */ + public static final String PASSWORD = "password"; // NOI18N + /** + * Common identifier used for requesting a host name. + */ + public static final String HOST = "host"; // NOI18N + /** + * Common identifier used for requesting a port number. + */ + public static final String PORT = "port"; // NOI18N + + /** + * Returns a requested property of a specified class or {@code null} if no + * property available and could not be retrieved. + * + *

It is guaranteed that this method is called from outside the EDT.
+ * Implementor could initiate UI interaction with a user to get needed + * input.

+ * + * @param uri the URI of the connection destination. + * @param clazz the expected class of the property. + * @param name the name of the requested property. + * @param misc any additional data that {@link Connector} may decide to + * provide to the SPI implementor to make it easier/possible to provide + * requested into. + * @return a requested property of the requested type or {@code null}, if + * property is not available. + */ + public T getProperty(URI uri, Class clazz, String name, Object... misc); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/ConnectionImplementation.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/ConnectionImplementation.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/ConnectionImplementation.java @@ -0,0 +1,98 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfoProvider; +import org.netbeans.modules.nativeexecution.spi.support.SignalSupportProvider; +import org.openide.util.Lookup; + +/** + * Interface to be implemented by SPI providers. + * + * @author akrasny + */ +public interface ConnectionImplementation { + + /** + * Creates a new {@link NativeProcessCreator} instance. + * + * @return a new not configured {@link NativeProcessCreator} + * @throws NativeExecutionException if some problem occurs + */ + public NativeProcessCreator newProcessBuilderImpl() throws NativeExecutionException; + + /** + * Returns a {@link Lookup} associated with the connection. + * + *

For now this lookup is not exposed to the API users. It is used by + * the infrastructure to get implementation of SPI services providers for + * the connection.

+ * + * @return the {@link Lookup} associated with this connection. + * + * @see ConnectionInfoProvider + * @see SignalSupportProvider + */ + public Lookup getLookup(); + + /** + * A request to close the connection. + */ + public void disconnect(); + + /** + * Returns an actual URI this connection is associated with. + * + * @return an URI this connection is associated with. + */ + public URI getURI(); + + /** + * Tests if the connection is alive. + * + * @return true if connection is established, false otherwise. + */ + public boolean isConnected(); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/ConnectorImplementation.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/ConnectorImplementation.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/ConnectorImplementation.java @@ -0,0 +1,92 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; + +/** + * SPI for providing a way of {@link Connection} establishment. + * + *

SPI implementation must be registers in the global lookup within a + * {@code "ConnectionService/"} path.

+ * + *

Once the {@link ConnectionManager} is asked to establish a new connection + * with the destination identified by an URI, it extracts a scheme part + * of the URI and looks for an implementor of this SPI in the corresponding + * lookup path to pass control over to it.

+ * + * @author akrasny + */ +public interface ConnectorImplementation { + + /** + * A request to establish a new connection. + * + *

It is guaranteed that:

  • method is called from outside the + * EDT;
  • method is called non concurrently;

+ * + *

Implementation should return a new instance on each subsequent + * call.

+ * + *

It is not required that ConnectorImplementation uses the originally passed URI + * for establishing a connection. ConnectorImplementation may choose to re-direct a + * request to a different connector with a different URI.

+ * + *

Normally ConnectorImplementation should not directly interact with a user. + * ConnectorImplementation just asks authDataProvider to provide all needed + * information.

+ * + * + * @param uri an URI of the requested destination. + * @param authDataProvider the provider of information needed for connection + * establishment. + * @return a new instance of {@link ConnectionImplementation} that represents newly + * established connection. + * @throws NativeExecutionException is thrown if connection cannot be + * established for some reason. + * @throws InterruptedException if calling thread was interrupted. + */ + public ConnectionImplementation connectTo(final URI uri, final AuthDataProvider authDataProvider) throws InterruptedException, NativeExecutionException; +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/URIIdentifier.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/URIIdentifier.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/URIIdentifier.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; + +/** + * SPI provider may register an identifier for URIs of a certain kind (certain + * scheme). + * + *

Should be registered in the global Lookup within a + * {@code ConnectionService/} path.

+ * + *

{@link ConnectionManager} uses registered URIIdentifiers to compare URIs + * equality. As an example URIs {@code ssh://user:password@host} and {@code ssh://user@host} + * could denote the same connection end point.

+ * + *

If no Identifier registered for a particular scheme, URI's equals() method + * is used.

+ * + * @author akrasny + */ +public interface URIIdentifier { + + /** + * Tests whether these URIs denote the same connection. + * + * @param uri1 the first URI to compare + * @param uri2 the second URI to compare + * @return true if these URIs are the same from SPI provider point of view. + */ + public boolean areIdentical(final URI uri1, final URI uri2); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessCreator.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessCreator.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessCreator.java @@ -0,0 +1,76 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi.process; + +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; + +/** + * An SPI interface for providing process creator implementation. + * + * @author akrasny + */ +public interface NativeProcessCreator { + + /** + * Returns a map of non-standard attributes supported by this creator. + * + * @return the map of supported non-standard attributes. + * @see NativeProcessBuilder + */ + public Map> getSupportedProperties(); + + /** + * Takes parameters configured by a {@link NativeProcessBuilder} and starts + * a new process in accordance to that parameters. + * + * @param params parameters configured with a {@link NativeProcessBuilder}. + * Never {@code null}. + * + * @return a new started process implementation. + * + * @throws NativeExecutionException is thrown if process cannot be started. + */ + public NativeProcessImplementation createAndStart( + final NativeProcessParams params) throws NativeExecutionException; +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessImplementation.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessImplementation.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessImplementation.java @@ -0,0 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi.process; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * + * @author akrasny + */ +public interface NativeProcessImplementation { + + /** + * Returns a PID of the process. + * + * @return the PID of the process or -1 if real PID cannot be provided. + */ + public int getPID(); + + /** + * Returns the output stream of the process. + * + *

Output to the stream should be piped into the input stream of the + * process.

+ * + * @return the output stream connected to the input of the process. + */ + public OutputStream getOutputStream(); + + /** + * Returns the input stream of the process. + * + *

The stream should obtain data piped from the output stream of the + * process.

+ * + * @return the input stream connected to the output stream of the process. + */ + public InputStream getInputStream(); + + /** + * Returns the error stream of the process. + * + *

The stream should obtain data piped from the error output stream of + * the process.

+ * + * @return the input stream connected to the error stream of the process. + */ + public InputStream getErrorStream(); + + /** + * Implementation should cause the current thread to wait, if necessary, + * until the process has terminated. + * + *

This method should return immediately if the process has already + * terminated. If the process has not yet terminated, the calling thread + * should be blocked until the process exits.

+ * + * @return the exit value of the process. By convention, 0 indicates normal + * termination. + * @throws InterruptedException if the current thread is interrupted by + * another thread while it is waiting, then the wait is ended and an + * InterruptedException is thrown. + */ + public int waitResult() throws InterruptedException; + + /** + * Implementation-specific termination of the underlying system process. + * + *

It is guaranteed that this method is called only once. Implementation + * should not (but may) wait for the actual termination before returning + * from the call.

+ */ + public void destroy(); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessParams.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessParams.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/process/NativeProcessParams.java @@ -0,0 +1,208 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi.process; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.netbeans.modules.nativeexecution.access.NativeProcessParamsFactory; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.process.NativeProcess; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; + +/** + * Parameters constructed by a {@link NativeProcessBuilder} to be used for + * {@link NativeProcess} creation. + * + *

This is an immutable object.

+ * + * @author akrasny + */ +public final class NativeProcessParams { + + private final Connection connection; + private final Map connectionInfo; + private final Map properties; + private final Map environment; + private final List command; + private final boolean redirectError; + private final String shellScript; + private final String shell; + private final String wdir; + + private NativeProcessParams(Connection connection, Map connectionInfo, + Map environmentMap, Map properties, + List command, String shell, String shellScript, + boolean redirectError, String wdir) { + this.connection = connection; + this.connectionInfo = connectionInfo; + this.environment = environmentMap; + this.properties = properties; + this.command = command; + this.shell = shell; + this.shellScript = shellScript; + this.redirectError = redirectError; + this.wdir = wdir; + } + + /** + * Returns an immutable list of a command to be started and it's arguments. + * + *

There are two mutually exclusive attributes - a shell script and a + * list of a command with it's arguments. Only one of these attributes could + * be set.

+ * + * @return an immutable list of a command and it's arguments. + */ + public List getCommand() { + return Collections.unmodifiableList(command); + } + + /** + * Returns a shell script to be started. + * + *

There are two mutually exclusive attributes - a shell script and a + * list of a command with it's arguments. Only one of these attributes could + * be set.

+ * + * @return a shell script to start. + */ + public String getShellScript() { + return shellScript; + } + + /** + * A shell to be used to start a specified shell script (if any). + * + * @see #getShellScript() + * @return a path to the shell to be used to start a specified shell script. + */ + public String getShell() { + return shell; + } + + /** + * Returns an immutable map of all attributes associated with the current + * {@link Connection}. + * + * @return an immutable map of all attributes associated with the current + * {@link Connection}. + */ + public Map getConnectionInfo() { + return Collections.unmodifiableMap(connectionInfo); + } + + /** + * Returns a requested property of a specified class or {@code null} if no + * property with this name defined. + * + * @param clazz the expected class of the property. + * @param propertyName the name of the requested property. + * + * @return a requested property of the requested type or {@code null}, if + * property is not defined. + */ + public T getProperty(Class clazz, String propertyName) { + Object val = properties.get(propertyName); + if (val != null && clazz.isAssignableFrom(val.getClass())) { + return clazz.cast(val); + } + return null; + } + + /** + * A working directory process should be started in. + * + * @return the working directory where process should be started. + */ + public String getWorkingDirectory() { + return wdir; + } + + /** + * Tells whether standard error and standard output of a process should be + * merged together. + * + * @return {@code true} if output and error streams should be merged. + */ + public boolean isRedirectError() { + return redirectError; + } + + /** + * Returns configured immutable map of environment variables to be set + * before process creation. + * + * @return an immutable map of environment variables. + */ + public Map getEnvironment() { + return Collections.unmodifiableMap(environment); + } + + /** + * Returns a {@link Connection} this process should be started in. + * + * @return the {@link Connection} this process should be started in. + */ + public Connection getConnection() { + return connection; + } + + // + static { + NativeProcessParamsFactory.setDefault(new NativeProcessParamsFactoryImpl()); + } + + private static class NativeProcessParamsFactoryImpl extends NativeProcessParamsFactory { + + @Override + public NativeProcessParams newNativeProcessParams(Connection connection, + Map connectionInfo, Map env, + Map properties, List command, + String shell, String shellScript, boolean redirectError, String wdir) { + NativeProcessParams params = new NativeProcessParams(connection, connectionInfo, env, + properties, command, shell, shellScript, redirectError, wdir); + return params; + } + } + // +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/ConnectionInfo.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/ConnectionInfo.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/ConnectionInfo.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi.support; + +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.util.HostInfo; +import org.openide.util.Lookup; + +/** + * Connection info is just a map of String-String properties and an initial + * {@link EnvironmentMap} of the {@link Connection}. + * + *

{@link ConnectionImplementation} SPI implementors use this interface to provide a + * map of connection-specific properties. For this implementations of {@link ConnectionInfoProvider} + * should be put into connection's {@link Lookup} (See {@link ConnectionImplementation}). + * Information from all SPI implementors is collected and merged altogether by + * the execution infrastructure before making a final association with the {@link Connection}.

+ * + *

Note: {@link HostInfo} is constructed from the known subset of these + * properties. SPI implementor should make sure that expected properties are + * provided if they want HostInfo service to provide a meaningful + * information.

+ * + * @see HostInfo + */ +public interface ConnectionInfo { + + /** + * Returns an EnvironmentMap filled with variables discovered by the + * implementation. + * + * @return the populated EnvironmentMap. + */ + public EnvironmentMap getEnvironmentMap(); + + /** + * Returns a map of attributes to be associated with the connection. + * + * @return the map of connection attributes. + */ + public Map getProperties(); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/ConnectionInfoProvider.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/ConnectionInfoProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/ConnectionInfoProvider.java @@ -0,0 +1,94 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi.support; + +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.util.HostInfo; + +/** + * + * Provides a discovered list of connection-specific properties. + * + *

Among other usages, this information is used to construct a {@link HostInfo} + * object to provide an easy access to the most common host-specific + * attributes.

+ * + *

Implementors of {@link ConnectionInfoProvider} could provide following + * properties that are used by the {@link HostInfo} class:

+ * + * + *
+ * ------------+------------------------------------------------------
+ *    Name     |   Possible values (otherwise will be set to UNKNOWN)
+ * ------------+------------------------------------------------------
+ * CPUFAMILY   |  SPARC, X86
+ * BITNESS     |  32, 64
+ * OSFAMILY    |  SOLARIS, LINUX, WINDOWS, MACOSX
+ * OSBUILD     |  <any string>
+ * OSNAME      |  <any string>
+ * CPUNUM      |  <any positive integer>
+ * 
+ * + * @author akrasny + */ +public interface ConnectionInfoProvider { + + /** + * A method to be implemented by an SPI provider. + * + *

It is guaranteed that this method is invoked outside the EDT and not + * concurrently for the same connection.

+ * + *

Implementor should not do any caching of the collected data. This + * method is called at least once during a connection establishment process, + * but could be called again if implicit refresh is requested.

+ * + *

Note: gathering {@link ConnectionInfo} from the SPI implementors is a + * part of connection process and {@link Connection} is considered to be + * established (and publicly available) when only after all providers are + * done with this task.

+ * + * @return a {@link ConnectionInfo} discovered by an implementor. + */ + public ConnectionInfo getConnectionInfo(Connection connection) throws NativeExecutionException; +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/SignalSupport.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/SignalSupport.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/SignalSupport.java @@ -0,0 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi.support; + +import org.netbeans.modules.nativeexecution.api.process.NativeProcess; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.util.SignalUtils.Signal; +import org.openide.util.Lookup; + +/** + * + * An extension point for providing a signals support. + * + *

Implementors of the {@link ConnectionImplementation} SPI could register their + * implementation of the + * {@link SignalSupportProvider} in the {@link Lookup} associated with the + * connection.

+ * + *

By convention if implementation cannot perform a task, + * {@link UnsupportedOperationException} is thrown.

+ * + * @author akrasny + */ +public interface SignalSupport { + + /** + * A request to send a signal to a specific process. + * + * @param process {@link NativeProcess} to signal. + * @param signal the {@link Signal} to be send. + */ + public void signal(NativeProcess process, Signal signal); + + /** + * A request to send a signal to a group the process belongs to. + * + * @param process {@link NativeProcess} which group should receive a signal. + * @param signal the {@link Signal} to be send. + */ + public void signalGrp(NativeProcess process, Signal signal); + + /** + * A request to send a signal with an attached value to a specific process. + * + * @param process {@link NativeProcess} to signal. + * @param signal the {@link Signal} to be send. + * @param value the value to send together with the signal. + */ + public void sigqueue(NativeProcess process, Signal signal, int value); + + /** + * A request to send a signal to a specific process. + * + * @param pid a PID of a process to signal. + * @param signal the {@link Signal} to be send. + */ + public void signal(int pid, Signal signal); + + /** + * A request to send a signal to a group the process belongs to. + * + * @param pid the PID of a process which group should receive a signal. + * @param signal the {@link Signal} to be send. + */ + public void signalGrp(int pid, Signal signal); + + /** + * A request to send a signal with an attached value to a specific process. + * + * @param pid the PID of a process to signal. + * @param signal the {@link Signal} to be send. + * @param value the value to send together with the signal. + */ + public void sigqueue(int pid, Signal signal, int value); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/SignalSupportProvider.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/SignalSupportProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/spi/support/SignalSupportProvider.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.spi.support; + +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.openide.util.Lookup; + +/** + * Provider of {@link SignalSupport} for a specific {@link Connection}. + * + *

Implementors of the {@link ConnectionImplementation} SPI could register their + * implementation of the {@link SignalSupportProvider} in the {@link Lookup} + * associated with the connection.

+ * + * @author akrasny + */ +public interface SignalSupportProvider { + + /** + * Returns a {@link SignalSupport} implementation for a specific {@link Connection}. + * + * @param connection connection to get {@link SignalSupport} for. + * @return the {@link SignalSupport} implementation for the specified {@link Connection}. + * + */ + public SignalSupport getSignalSupport(Connection connection); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/Computable.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/Computable.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/Computable.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; + +/** + * An interface for calculating a result based on a single parameter. + * + * This interface is most useful in conjuction with the {@link TasksCachedProcessor} + * class. + * + * @param

the parameter type. + * @param the result type. + * + * @see TasksCachedProcessor + * @author akrasny + */ +public interface Computable { + + /** + * A method that does an actual computation. + * + * @param taskArguments the input parameter. + * @return a result of a computation. + * @throws NativeExecutionException in case of any problem occur during the + * computation. + */ + R compute(P taskArguments) throws NativeExecutionException; +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/HostInfo.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/HostInfo.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/HostInfo.java @@ -0,0 +1,289 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.util.Collections; +import java.util.Map; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.common.ConnectionsInfo; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfo; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfoProvider; + +/** + * Utility class to extract commonly used information about a host from data + * provider by the {@link ConnectionInfoProvider} SPI implementors. + * + *

This class gives an easy access to the most common host-specific + * attributes.

+ * + *

Note: It is supposed that in most cases there is some host behind a + * {@link Connection}. Theoretically there could be connections where host info + * (entirely or partially) is not applicable. For such connections HostInfo + * attributes will be set to {@code "UNKNOWN"} value.

+ * + * @see ConnectionInfoProvider + * + * @author akrasny + */ +public final class HostInfo { + + private static final String UNKNOWN = "UNKNOWN"; // NOI18N + private final Map info; + private final OS os; + private final int cpuNum; + private final CpuFamily cpuFamily; + + private HostInfo(Map info) { + this.info = info; + assert info != null; + OSFamily osFamily = get("OSFAMILY", OSFamily.UNKNOWN); // NOI18N + Bitness bitness = get("BITNESS", Bitness._32); // NOI18N + String osBuild = get("OSBUILD", UNKNOWN); // NOI18N + String osName = get("OSNAME", UNKNOWN); // NOI18N + cpuFamily = get("CPUFAMILY", CpuFamily.UNKNOWN); // NOI18N + cpuNum = get("CPUNUM", 1); // NOI18N + os = new OS(osFamily, osName, osBuild, bitness); + } + + /** + * Getter of HostInfo object for a specified Connection. + * + * @param connection connection to get info for. + * @return a HostInfo to the connection. + */ + public static HostInfo getFor(final Connection connection) { + // TODO: optimize - do some caching + ConnectionInfo ci = ConnectionsInfo.getConnectionInfo(connection.getURI()); + return ci == null ? new HostInfo(Collections.emptyMap()) + : new HostInfo(ci.getProperties()); + } + + /** + * Returns an OS object + * + * @return the OS object + */ + public OS getOS() { + return os; + } + + /** + * Returns a number of CPUs on the host. + * + * @return the number of CPUs on the host. + */ + public int getCpuNum() { + return cpuNum; + } + + /** + * Returns CpuFamily information. + * + * @return CpuFamily information. + */ + public CpuFamily getCpuFamily() { + return cpuFamily; + } + + /** + * Test weather this is a Windows system. + * + * @return true if and only if getOS().getFamily() is OSFamily.WINDOWS + */ + public boolean isWindows() { + return OSFamily.WINDOWS.equals(os.family); + } + + /** + * Tests weather this is a Unix-like system. + * + * @return true if OSFamily is one of LINUX, MACOSX or SOLARIS + */ + public boolean isUnix() { + switch (os.family) { + case LINUX: + case MACOSX: + case SOLARIS: + return true; + case WINDOWS: + return false; + case UNKNOWN: + return false; + default: + throw new IllegalStateException("Unexpected OSFamily: " + this); //NOI18N + } + } + + /** + * List of known CPU families property values. + */ + public enum CpuFamily { + + SPARC, X86, UNKNOWN; + } + + /** + * List of known OS families property values. + */ + public enum OSFamily { + + SOLARIS, LINUX, WINDOWS, MACOSX, UNKNOWN; + } + + /** + * Bitness of the OS kernel - either 32 or 64 bits. + */ + public enum Bitness { + + _32, + _64; + + static Bitness valueOf(int bitness) { + return bitness == 64 ? _64 : _32; + } + + @Override + public String toString() { + return (this == _32) ? "32" : "64"; // NOI18N + } + } + + /** + * Information about an operating system. + */ + public static final class OS { + + private final OSFamily family; + private final String name; + private final String version; + private final Bitness bitness; + + /** + * Constructs new OS object with provided attributes. + * + * @param family OS family + * @param name OS name + * @param version OS version string + * @param bitness OS operating bitness mode. + */ + private OS(OSFamily family, String name, String version, Bitness bitness) { + this.family = family; + this.name = name; + this.version = version; + this.bitness = bitness; + } + + /** + * Returns a bitness of the OS kernel. + * + * @return the bitness of the OS kernel + */ + public Bitness getBitness() { + return bitness; + } + + /** + * Returns an OS family attribute. + * + * @return the OS family attribute. + */ + public OSFamily getFamily() { + return family; + } + + /** + * Returns a name of the OS + * + * @return the name of OS. + */ + public String getName() { + return name; + } + + /** + * Returns a version string of the OS. + * + * @return the version string of the OS. + */ + public String getVersion() { + return version; + } + } + + // + @SuppressWarnings("unchecked") + private > T get(String name, T defaultValue) { + String val = info.get(name); + if (val == null) { + return defaultValue; + } + + try { + T result = (T) Enum.valueOf(defaultValue.getClass(), val.toUpperCase()); + return result; + } catch (Throwable ex) { + return defaultValue; + } + } + + private String get(String name, String defaultValue) { + String val = info.get(name); + if (val == null) { + return defaultValue; + } + return val; + } + + private int get(String name, int defaultValue) { + String val = info.get(name); + if (val == null) { + return defaultValue; + } + try { + int res = Integer.parseInt(val); + return res; + } catch (Throwable th) { + return defaultValue; + } + } + // +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/MacroExpander.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/MacroExpander.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/MacroExpander.java @@ -0,0 +1,191 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.text.ParseException; +import java.util.Map; + +/** + * A simple class that expands all macros in a String using definitions taken + * from a dictionary map. + * + * @author akrasny + */ +public final class MacroExpander { + + private static final int[][] ttable = new int[][]{ + {0, 0, 0, 1, 0, 0}, + {2, 3, 3, 10, 4, 3}, + {2, 2, 5, 6, 5, 5}, + {7, 7, 8, 8, 8, 9}, + {7, 3, 3, 3, 8, 8} + }; + + private MacroExpander() { + } + + private static int getCharClass(char c) { + if (c == '_' || (c >= 'A' && c <= 'Z') || c >= 'a' && c <= 'z') { + return 0; + } + + if (c >= '0' && c <= '9') { + return 1; + } + + if (c == '$') { + return 3; + } + + if (c == '{') { + return 4; + } + + if (c == '}') { + return 5; + } + + return 2; + } + + private static String valueOf(String macro, Map map) { + String result = map.get(macro); + return result == null ? "${" + macro + "}" : result; // NOI18N + } + + /** + * Parses the given string and replaces all it's macros with values from the + * provided dictionary. + * + *

Macros in the form {@code $NAME} and {@code ${NAME}} are + * supported.

+ * + *

Any macro that is not in the dictionary is left in the string (in the + * {@code ${NAME}} form).

+ * + *

Current implementation does a single pass of expansion:
+ * + * the result of a string {@code "C=${B}"} expansion with a dictionary + * {@code ["A=VALUE", "B=$A"]} is {@code "C=${A}"}.

+ * + * @param stringToExpand string with macros to be expanded. + * @param dictionary key-value pairs of macros definition. + * @return a string with all macros substituted with their values taken from + * the dictionary. + * @throws ParseException if string cannot be parsed + */ + public static String expandMacros(final String stringToExpand, + final Map dictionary) throws ParseException { + + if (stringToExpand == null || stringToExpand.length() == 0) { + return stringToExpand; + } + + StringBuilder res = new StringBuilder(); + StringBuilder buf = new StringBuilder(); + + int state = 0, pos = 0, mpos = -1; + int length = stringToExpand.length(); + char c; + + while (pos <= length) { + c = pos == length ? 0 : stringToExpand.charAt(pos); + + switch (ttable[state][getCharClass(c)]) { + case 0: + if (c != 0) { + res.append(c); + } + break; + case 1: + mpos = pos; + buf.setLength(0); + state = 1; + break; + case 2: + buf.append(c); + state = 2; + break; + case 3: + res.append(stringToExpand.subSequence(mpos, pos + (c == 0 ? 0 : 1))); + buf.setLength(0); + state = 0; + break; + case 4: + state = 4; + break; + case 5: + res.append(valueOf(buf.toString().trim(), dictionary)); + pos--; + buf.setLength(0); + state = 0; + break; + case 6: + res.append(valueOf(buf.toString().trim(), dictionary)); + mpos = pos; + buf.setLength(0); + state = 1; + break; + case 7: + buf.append(c); + state = 3; + break; + case 8: + throw new ParseException("Bad substitution", pos); // NOI18N + case 9: + res.append(valueOf(buf.toString().trim(), dictionary)); + buf.setLength(0); + state = 0; + break; + case 10: + res.append(stringToExpand.subSequence(mpos, pos)); + pos--; + buf.setLength(0); + state = 0; + break; + } + pos++; + } + + return res.toString(); + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/MacroMap.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/MacroMap.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/MacroMap.java @@ -0,0 +1,316 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.io.PrintStream; +import java.text.ParseException; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +/** + * Map of key-value String pairs. + * + * An implementation of Map interface that can do macro expansion on insertion + * and preserves an order of insertions. + * + * The following formats of macros are supported: + *
+ * ${NAME}
+ * $NAME
+ * 
+ * + * Not thread safe + * + * @author akrasny + */ +public class MacroMap implements Cloneable { + + private Map dictionary; + private final LinkedHashMap map; + private final Comparator comparator; + + /** + * Creates a new map with no specific keys comparator. The result is the + * same as if {@code MacroMap(null)} is called. + */ + public MacroMap() { + this((Comparator) null); + } + + /** + * Creates a new map with a specified keys comparator. + * + * @param comparator the comparator to be used in keys comparisons. + */ + public MacroMap(Comparator comparator) { + this.comparator = comparator; + this.map = new LinkedHashMap(); + } + + protected MacroMap(MacroMap map) { + this.comparator = map.comparator; + this.map = new LinkedHashMap(); + for (Map.Entry entry : map.entrySet()) { + this.map.put(entry.getKey(), entry.getValue()); + } + } + + /** + * Adds a new key-value pair to the map. + * + *

The {@link #put(String, String)} method works as follows: + * + * + * MacroMap m = new MacroMap() + * m.put("ANT_HOME", "/bin"); + * m.put("PATH", "$ANT_HOME"); + * m.put("PATH", "${PATH}:/usr/bin"); + * + * + * as a result the key "PATH" will be associated with the value + * "${ANT_HOME}:/usr/bin".

+ * + *

The only (possibly) altered value on insertion is the inserted value + * itself. The only macro that is expanded is the key-named macro. (See the + * above example - $ANT_HOME is not expanded in the value of PATH)

+ * + *

Comparator is used for comparing key.

+ * + * @param key the key of an entry + * @param value the new value of an entry + * @return a previous value associated with the key + */ + public final String put(String key, String value) { + if (comparator != null) { + for (String k : map.keySet()) { + if (comparator.compare(k, key) == 0) { + return putImpl(k, value); + } + } + } + return putImpl(key, value); + } + + /** + * Returns a value associated with a key. + * + * @param key the key to get value for or {@code null} if no value is + * associated. + * @return the value associated with a key. + */ + public final String get(Object key) { + if (comparator != null) { + for (String k : map.keySet()) { + if (comparator.compare(k, (String) key) == 0) { + return map.get(k); + } + } + } + return map.get((String) key); + } + + private String putImpl(String key, String value) { + String result = value; + + TreeMap oneElementMap = comparator == null + ? new TreeMap() + : new TreeMap(comparator); + + String val = get(key); + + if (val != null) { + oneElementMap.put(key, val); + } + + if (dictionary != null) { + oneElementMap.putAll(dictionary); + } + + try { + result = MacroExpander.expandMacros(value, oneElementMap); + } catch (ParseException ex) { + } + + return map.put(key, result); + } + + /** + * Returns a new map, based on this map, but with all possible macros + * expanded. + * + * This method tries to expand all macros in the map. Any recursion will be + * left unexpanded. Unknown macros will remain untouched. + * + * @return a new map with expanded macros. + */ +// public final Map reduce() { +// throw new NotImplementedException("Not implemented yet"); +// } + /** + * Defines a NAME-VALUE pairs that are to be used in expansion while not + * being a part of this map. + * + * @param dictionary an additional key-value entries that are used in + * expansion. + */ + public final void setDictionary(Map dictionary) { + this.dictionary = new HashMap(dictionary); + } + + /** + * A string representation of the map. + * + * @return the string representation. + */ + @Override + public final String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("{"); // NOI18N + + for (Map.Entry entry : entrySet()) { + buf.append(entry.getKey()); + buf.append(" = "); // NOI18N + buf.append(entry.getValue()); + buf.append(", "); // NOI18N + } + + buf.append("}"); // NOI18N + return buf.toString(); + } + + /** + * A convenient way for printing out the map. + * + * @param out stream to out map to. + */ + public final void dump(PrintStream out) { + out.println("---- Map Dump ----"); // NOI18N + for (Map.Entry entry : entrySet()) { + out.println(entry.getKey() + " = " + entry.getValue()); // NOI18N + } + out.println("------------------"); // NOI18N + } + + /** + * Removes entry from the map. + * + * @param key the key to remove entry for. + * @return a value previously associated with the key. + */ + public final String remove(Object key) { + if (!(key instanceof String)) { + return null; + } + + if (comparator != null) { + for (String k : map.keySet()) { + if (comparator.compare(k, (String) key) == 0) { + key = k; + break; + } + } + } + return map.remove((String) key); + } + + /** + * Returns an entry set of the map in the same order as they have been + * added. + * + * @return the entry set. + */ + public final Set> entrySet() { + return map.entrySet(); + } + + /** + * Returns the number of key-value mappings in this map. + * + * @return the number of key-value mappings in this map + */ + public final int size() { + return map.size(); + } + + /** + * Returns {@code true} if this map contains no key-value mappings. + * + * @return {@code true} if this map contains no key-value mappings + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * Removes all entries from this map. + * + * The map becomes empty after this call returns. + */ + public void clear() { + map.clear(); + } + + /** + * Returns an unmodifiable copy of this map as a {@code Map} + * collection. + * + * @return unmodifiable copy + */ + public Map toMap() { + return Collections.unmodifiableMap(new LinkedHashMap(map)); + } + + /** + * Returns a deep copy of the MacroMap. + */ + @Override + public MacroMap clone() throws CloneNotSupportedException { + MacroMap clone = (MacroMap) super.clone(); + return new MacroMap(clone); + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessInputProvider.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessInputProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessInputProvider.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.io.BufferedWriter; +import org.netbeans.modules.nativeexecution.api.process.NativeProcess; + +/** + * Used in {@link ProcessUtils} to provide an input to a started process. + * + * @author akrasny + */ +public interface ProcessInputProvider { + + /** + * Is called once {@link NativeProcess} is started. + * + * An encoding of the passed BufferedWriter is configured based on used + * ProcessBuilder's attributes (or set to "UTF-8" if not configured + * otherwise). + * + * @param bw BufferedWriter that wraps process's output stream. + */ + public void start(BufferedWriter bw); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessOutputProcessor.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessOutputProcessor.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessOutputProcessor.java @@ -0,0 +1,59 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +/** + * Used in {@link ProcessUtils} to process an output of a started process. + * + * @author akrasny + */ +public interface ProcessOutputProcessor { + + /** + * Is called for each line of the process's output. + * + * The line's coding is a process's output encoding... + * + * @param line the line of process's output. + */ + public void processLine(final CharSequence line); +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessUtils.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessUtils.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/ProcessUtils.java @@ -0,0 +1,591 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import org.netbeans.modules.nativeexecution.ExecutionLogger; +import org.netbeans.modules.nativeexecution.access.NativeProcessAccessor; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.api.process.NativeProcess; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; +import org.netbeans.modules.nativeexecution.util.SignalUtils.Signal; +import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; +import org.openide.util.RequestProcessor.Task; + +/** + * Useful utilities that handle starting of an external process, handling it's + * streams and so on.. + * + * All methods are static. + * + * @author akrasny + */ +public final class ProcessUtils { + + private static final RequestProcessor RP = new RequestProcessor("ProcessUtils", 50); // NOI18N + + private ProcessUtils() { + } + + private static int waitAndProcess(final Process process, + final ProcessOutputProcessor outputProcessor, + final ProcessOutputProcessor errorProcessor, + final ProcessInputProvider inputProvider) throws NativeExecutionException { + final Task readOutput, readError; + Task writeInput = null; + boolean interrupted = false; + + try { + Charset charset = getProcessOutputCharset(process); + + readOutput = RP.post(new OutputReader("Read output from " + process.toString(), // NOI18N + process.getInputStream(), charset, outputProcessor)); + readError = RP.post(new OutputReader("Read error from " + process.toString(), // NOI18N + process.getErrorStream(), charset, errorProcessor)); + if (inputProvider != null) { + charset = getProcessInputCharset(process); + writeInput = RP.post(new InputWriter("Write input to " + process.toString(), // NOI18N + process.getOutputStream(), charset, inputProvider)); + } + + int result = -1; + + try { + result = process.waitFor(); + } catch (InterruptedException ex) { + // Means THIS thread was interrupted + Thread.interrupted(); + interrupted = true; + + // process is running, readers are running... + // need to stop all these activities as the only consumer of the + // execute() doesn't need it now ... + + process.destroy(); + + if (readOutput != null) { + readOutput.cancel(); + } + + if (readError != null) { + readError.cancel(); + } + } finally { + if (writeInput != null) { + writeInput.cancel(); + } + + if (readOutput != null) { + readOutput.waitFinished(); + } + + if (readError != null) { + readError.waitFinished(); + } + + if (writeInput != null) { + writeInput.waitFinished(); + } + } + + return result; + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + /** + * Starts a new process using already configured processBuilder, waits for + * its completion and returns it's exit code. + * + *

+ * outputProcessor or + * errorProcessor could be provided to process output/error + * streams. Their {@code ProcessOutputProcessor.processLine(CharSequence)} + * methods will be invoked (each in it's own thread) on each new line read + * from the process's output/error streams. Processors could be null - in + * this case streams still will be read-out, but disregarded.
+ * Character encoding to be used is taken from + * NativeProcessBuilder's parameter "outputCharset" of class + * Charset.class (or default, which is "UTF-8" on systems that support it, + * if not configured).

+ * + *

+ * inputProvider could be specified to feed a data to process's + * input stream. It's {@code ProcessInputProvider.start()} method will be + * invoked in a separate thread once process is started.
Character + * encoding to be used is taken from + * NativeProcessBuilder's parameter "inputCharset" of class + * Charset.class (or default, which is "UTF-8" on systems that + * support it, if not configured).

+ * + *

Threading Model:
+ * + * This is a blocking method. Invoking thread is blocked until it's + * completion.
Two or three additional threads will be started + * depending on weather inputProvider is specified or not. There is no any + * synchronization between those threads.
If outputProcessor stuck + * process may not finish (if internal buffer is not big enough to fit all + * the output). Process may finish before all it's output it processed. But + * this method will not return before that.

+ * + *

Thread interruption:
+ * + * If invoking thread is interrupted while it is waiting for the process to + * finish, the process will be terminated (See {@link #destroy(NativeProcess)}), + * all reader/writer threads will be terminated and method will return. + * Thread's interrupt status will be set to true. + * + * @param processBuilder configured process builder to use + * @param outputProcessor processor of process's output stream. Could be + * null. + * @param errorProcessor processor of process's error stream. Could be null. + * @param inputProvider writer to process's input stream. Could be null. + * @return process's exit code. By convention exit code 0 means successful + * execution. + * @throws NativeExecutionException in case of any problem + * NativeExecutionException is thrown. + */ + public static int execute(final NativeProcessBuilder processBuilder, + final ProcessOutputProcessor outputProcessor, + final ProcessOutputProcessor errorProcessor, + final ProcessInputProvider inputProvider) throws NativeExecutionException { + Process process; + try { + process = processBuilder.call(); + } catch (NativeExecutionException ex) { + throw ex; + } catch (Exception ex) { + throw new NativeExecutionException(ex); + } + return waitAndProcess(process, outputProcessor, errorProcessor, inputProvider); + } + + /** + * Starts a new process using already configured processBuilder, waits for + * its completion and returns it's exit code. + * + *

This is almost the same as {@link #execute(NativeProcessBuilder, + * ProcessOutputProcessor, ProcessOutputProcessor, ProcessInputProvider)} + * but works with + * java.lang.ProcessBuilder class.
Character encoding to + * be used is "UTF-8" on systems that support it, or whatever default is + * otherwise.

+ * + * @see #execute(NativeProcessBuilder, ProcessOutputProcessor, + * ProcessOutputProcessor, ProcessInputProvider) + * @param processBuilder configured process builder to use + * @param outputProcessor processor of process's output stream. Could be + * null. + * @param errorProcessor processor of process's error stream. Could be null. + * @param inputProvider writer to process's input stream. Could be null. + * @return process's exit code. By convention exit code 0 means successful + * execution. + * @throws NativeExecutionException in case of any problem + * NativeExecutionException is thrown. + */ + public static int execute(final ProcessBuilder processBuilder, + final ProcessOutputProcessor outputProcessor, + final ProcessOutputProcessor errorProcessor, + final ProcessInputProvider inputProvider) throws NativeExecutionException { + Process process; + try { + process = processBuilder.start(); + } catch (Exception ex) { + throw new NativeExecutionException("", ex); + } + return waitAndProcess(process, outputProcessor, errorProcessor, inputProvider); + } + + /** + * Starts a new process using already configured processBuilder, waits for + * its completion and returns it's ExitStatus.

+ * + * Usage pattern: + * + *

+     * ExitStatus status = ProcessUtils.execute(new ProcessBuilder("/bin/ls"));
+     * if (status.isOK()) {
+     *     do something...
+     * } else {
+     *     System.out.println("Error! " + status.error);
+     * }
+     * 
+ * + * This method doesn't provide any input to the process! So process's input + * stream is force-closed once process is started. This means that process + * that requires any input will fail. Otherwise invocation of such process + * would block the current thread.

Threading model and encodings to be + * used are the same as described in + * {@link #execute(NativeProcessBuilder, ProcessOutputProcessor, ProcessOutputProcessor, ProcessInputProvider)}. + *

+ * + * @param processBuilder configured process builder to be used + * @return ExitStatus exit status of the process with it's exit code, + * read-out output and error + * @throws NativeExecutionException in case of error + * NativeExecutionException is thrown + * @see #execute(NativeProcessBuilder, + * ProcessOutputProcessor,ProcessOutputProcessor, ProcessInputProvider). + */ + public static ExitStatus execute(final NativeProcessBuilder processBuilder) throws NativeExecutionException { + final StringBuilder output = new StringBuilder(); + final StringBuilder error = new StringBuilder(); + + int rc = execute(processBuilder, new ProcessOutputProcessor() { + + @Override + public void processLine(CharSequence line) { + output.append(line).append('\n'); + } + }, new ProcessOutputProcessor() { + + @Override + public void processLine(CharSequence line) { + error.append(line).append('\n'); + } + }, new ProcessInputProvider() { + + @Override + public void start(BufferedWriter bw) { + try { + bw.close(); + } catch (IOException ex) { + // ignore + } + } + }); + + return new ExitStatus(rc, output.subSequence(0, output.length()), error.subSequence(0, error.length())); + } + + /** + * Starts a new process using already configured processBuilder, waits for + * its completion and returns it's ExitStatus. + * + *

See {@link #execute(NativeProcessBuilder)} for details.
The only + * difference is that this method works with + * java.lang.ProcessBuilder and "UTF-8" encoding is used.

+ * + * @param processBuilder configured process builder to be used + * @return ExitStatus exit status of the process with it's exit code, + * read-out output and error + * @throws NativeExecutionException in case of error + * NativeExecutionException is thrown + * @see #execute(NativeProcessBuilder) + */ + public static ExitStatus execute(final ProcessBuilder processBuilder) throws NativeExecutionException { + final StringBuilder output = new StringBuilder(); + final StringBuilder error = new StringBuilder(); + + int rc = execute(processBuilder, new ProcessOutputProcessor() { + + @Override + public void processLine(CharSequence line) { + output.append(line).append('\n'); + } + }, new ProcessOutputProcessor() { + + @Override + public void processLine(CharSequence line) { + error.append(line).append('\n'); + } + }, new ProcessInputProvider() { + + @Override + public void start(BufferedWriter bw) { + try { + bw.close(); + } catch (IOException ex) { + // ignore + } + } + }); + + return new ExitStatus(rc, output.subSequence(0, output.length()), error.subSequence(0, output.length())); + } + + /** + * Kills the subprocess. + * + * The subprocess represented by this Process object is forcibly terminated. + * + * This method tries to terminate a process in several ways. The first + * attempt is to call it's {@code Process.destroy()} method. On some systems + * this may fail (i.e. if the process is in the syscall). On failure this + * method will try to send SIGINT to the process's group (on systems that + * support that). The last attempt is to send SIGKILL (in 5 seconds). + * + * This is not a (long) blocking method. Process may still remain alive on + * return. + * + * @param process process to terminate (not necessarily NativeProcess) + */ + public static void destroy(final NativeProcess process) { + // First attempt is just call destroy() on the process + process.destroy(); + + // In case the process is in a system call (sleep, read, for example) + // this will not have a desired effect - in this case + // will send SIGTERM signal.. + + try { + process.exitValue(); + // No exception means successful termination + return; + } catch (IllegalThreadStateException ex) { + } + + try { + SignalUtils.signalGrp(process, Signal.SIGINT); + RP.schedule(new Runnable() { + + @Override + public void run() { + try { + process.exitValue(); + // No exception means successful termination + return; + } catch (IllegalThreadStateException ex) { + } + try { + SignalUtils.signalGrp(process, Signal.SIGTERM); + } catch (UnsupportedOperationException ex) { + // ignore + } + } + }, 5, TimeUnit.SECONDS); + } catch (UnsupportedOperationException ex) { + // ignore + } + } + + /** + * Returns a PID of the passed process. + * + * @param p the Process to get PID for. + * @return a PID of the process or -1 if PID cannot be detected. + */ + public static int getPID(Process p) { + if (p instanceof NativeProcess) { + return ((NativeProcess) p).getPID(); + } + + // TODO: implement + return -1; + } + + private static Charset getProcessOutputCharset(Process process) { + return getCharset("outputCharset", process); // NOI18N + } + + private static Charset getProcessInputCharset(Process process) { + return getCharset("inputCharset", process); // NOI18N + } + + private static Charset getCharset(String paramName, Process process) { + + Charset charset = null; + + if (process instanceof NativeProcess) { + NativeProcess np = (NativeProcess) process; + NativeProcessAccessor access = NativeProcessAccessor.getDefault(); + NativeProcessParams params = access.getProcessParams(np); + charset = params.getProperty(Charset.class, paramName); + } + + if (charset == null || !Charset.isSupported(charset.name())) { + charset = Charset.isSupported("UTF-8") // NOI18N + ? Charset.forName("UTF-8") : // NOI18N + Charset.defaultCharset(); // NOI18N + ExecutionLogger.getInstance().log(Level.FINE, + "{0} is not set or is not supported - use {1}", // NOI18N + new Object[]{paramName, charset.name()}); + } + + return charset; + } + + /** + * Simple class that represents process's exit status: exit code and data + * read-out from it's output and error streams. + */ + public static final class ExitStatus { + + /** + * The exit code of the Process. + * + *

By convention, exit code {@code == 0} indicates normal + * termination.

+ */ + public final int exitCode; + /** + * The data read from process's error stream. + */ + public final CharSequence error; + /** + * The data read from process's output stream. + */ + public final CharSequence output; + + private ExitStatus(int exitCode, CharSequence output, CharSequence error) { + this.exitCode = exitCode; + this.error = error; + this.output = output; + } + + /** + * Tests weather the Process was terminated normally. + * + *

By convention, exit code {@code == 0} indicates normal termination. + *

+ * + *

This method return true if and only if exitCode == 0.

+ * + * @return true if and only if exitCode == 0. + */ + public boolean isOK() { + return exitCode == 0; + } + + /** + * Returns user-friendly string representation of the ExitStatus. + * + * @return string representation of the status. + */ + @Override + public String toString() { + return "ExitStatus " + "exitCode=" + exitCode + "\nerror=" + error + "\noutput=" + output; // NOI18N + } + } + + private static class OutputReader implements Runnable { + + private final ProcessOutputProcessor outputProcessor; + private final String name; + private final InputStream stream; + private final Charset charset; + + public OutputReader(String name, InputStream stream, Charset charset, ProcessOutputProcessor outputProcessor) { + this.name = name; + this.stream = stream; + this.outputProcessor = outputProcessor; + this.charset = charset; + } + + @Override + public void run() { + BufferedReader br = null; + + try { + Thread.currentThread().setName(name); + br = new BufferedReader(new InputStreamReader(stream, charset)); + String line; + while ((line = br.readLine()) != null) { + if (outputProcessor != null) { + outputProcessor.processLine(line); + } + } + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + } + } + } + + private static class InputWriter implements Runnable { + + private final String name; + private final OutputStream stream; + private final ProcessInputProvider outputProcessor; + private final Charset charset; + + public InputWriter(String name, OutputStream stream, Charset charset, ProcessInputProvider inputProvider) { + this.name = name; + this.stream = stream; + this.outputProcessor = inputProvider; + this.charset = charset; + } + + @Override + public void run() { + BufferedWriter bw = null; + + try { + Thread.currentThread().setName(name); + OutputStreamWriter osw = new OutputStreamWriter(stream, charset); + bw = new BufferedWriter(osw); + outputProcessor.start(bw); + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + } finally { + if (bw != null) { + try { + bw.close(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + } + } + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/SignalUtils.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/SignalUtils.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/SignalUtils.java @@ -0,0 +1,260 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.access.ConnectionAccessor; +import org.netbeans.modules.nativeexecution.access.NativeProcessAccessor; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.process.NativeProcess; +import org.netbeans.modules.nativeexecution.spi.support.SignalSupport; +import org.netbeans.modules.nativeexecution.spi.support.SignalSupportProvider; +import org.openide.util.Lookup; + +/** + * Some systems support Signal mechanism for sending asynchronous events to a + * given process. + * + * This is an API class for delivering signals to a process. + * + * Note: signals-related functionality could be unavailable on some systems or + * irrelevant to some processes. + * + * @author akrasny + */ +public final class SignalUtils { + + private static final NativeProcessAccessor access = NativeProcessAccessor.getDefault(); + + private SignalUtils() { + } + + /** + * Send a specified signal to a process. + * + * @param process the process to signal + * @param signal the signal to send + * @throws UnsupportedOperationException + */ + public static void signal(NativeProcess process, Signal signal) throws UnsupportedOperationException { + Connection c = access.getConnection(process); + SignalSupport ss = getSignalSupport(c); + if (ss != null) { + ss.signal(process, signal); + } + } + + /** + * Send a signal with attached value to a process. + * + * @param process the process to signal + * @param signal the signal to send + * @param value the value to attach to a signal + * @throws UnsupportedOperationException + */ + public static void sigqueue(NativeProcess process, Signal signal, int value) throws UnsupportedOperationException { + Connection c = access.getConnection(process); + SignalSupport ss = getSignalSupport(c); + if (ss != null) { + ss.sigqueue(process, signal, value); + } + } + + /** + * Send a signal to a whole group a process belongs to. + * + * @param process the process (and it's group) to signal + * @param signal the signal to send + * @throws UnsupportedOperationException + */ + public static void signalGrp(NativeProcess process, SignalUtils.Signal signal) throws UnsupportedOperationException { + Connection c = access.getConnection(process); + SignalSupport ss = getSignalSupport(c); + if (ss != null) { + ss.signalGrp(process, signal); + } + } + + /** + * Send a specified signal to a PID in context of the passed Connection. + * + * @param destination the context URI + * @param pid the PID of the process to send signal to + * @param signal the signal to send + * @throws UnsupportedOperationException + */ + public void signal(URI destination, int pid, SignalUtils.Signal signal) throws UnsupportedOperationException { + SignalSupport ss = getSignalSupport(destination); + if (ss != null) { + ss.signal(pid, signal); + } + } + + /** + * Send a specified signal with an attached value to a PID in context of the + * passed Connection. + * + * @param destination the context URI + * @param pid the PID of the process to send signal to + * @param signal the signal to send + * @param value the value to attach to the signal + * @throws UnsupportedOperationException + */ + public void sigqueue(URI destination, int pid, SignalUtils.Signal signal, int value) throws UnsupportedOperationException { + SignalSupport ss = getSignalSupport(destination); + if (ss != null) { + ss.sigqueue(pid, signal, value); + } + } + + /** + * Send a signal to a whole group a process identified by PID belongs to in + * context of the passed Connection. + * + * @param destination the context URI + * @param pid the PID of the process to send signal to (as well as to it's + * group) + * @param signal the signal to send + * @throws UnsupportedOperationException + */ + public void signalGrp(URI destination, int pid, SignalUtils.Signal signal) throws UnsupportedOperationException { + SignalSupport ss = getSignalSupport(destination); + if (ss != null) { + ss.signalGrp(pid, signal); + } + } + + private static SignalSupport getSignalSupport(Connection connection) { + Lookup lookup = ConnectionAccessor.getDefault().getConnectionLookup(connection); + if (lookup == null) { + throw new UnsupportedOperationException(); + } + + SignalSupportProvider signalSupportProvider = lookup.lookup(SignalSupportProvider.class); + if (signalSupportProvider == null) { + throw new UnsupportedOperationException(); + } + + return signalSupportProvider.getSignalSupport(connection); + } + + private static SignalSupport getSignalSupport(URI destination) throws UnsupportedOperationException { + Connection connection = ConnectionManager.getConnection(destination); + if (connection == null) { + return null; + } + return getSignalSupport(connection); + } + + /** + * Available signals + */ + public enum Signal { + + NULL(0), + SIGHUP(1), + SIGINT(2), + SIGQUIT(3), + SIGILL(4), + SIGTRAP(5), + SIGABRT(6), + SIGEMT(7), + SIGFPE(8), + SIGKILL(9), + SIGBUS(10), + SIGSEGV(11), + SIGSYS(12), + SIGPIPE(13), + SIGALRM(14), + SIGTERM(15), + SIGUSR1(16), + SIGUSR2(17), + SIGCHLD(18), + SIGPWR(19), + SIGWINCH(20), + SIGURG(21), + SIGPOLL(22), + SIGSTOP(23), + SIGTSTP(24), + SIGCONT(25), + SIGTTIN(26), + SIGTTOU(27), + SIGVTALRM(28), + SIGPROF(29), + SIGXCPU(30), + SIGWAITING(32), + SIGLWP(33), + SIGFREEZE(34), + SIGTHAW(35), + SIGCANCEL(36), + SIGLOST(37), + SIGXRES(38), + SIGJVM1(39); + private final int id; + + private Signal(int id) { + this.id = id; + } + + /** + * Returns an integer value of the signal. + * + * This is OS-dependent value. + * + * @param os + * @return a numeric value of the signal. + */ + public int getID(HostInfo.OSFamily os) { + return id; + } + + /** + * Returns signal name + * + * @return signal name + */ + public String getID() { + return id == 0 ? "0" : name().substring(4); // NOI18N + } + } +} \ No newline at end of file diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/TasksCachedProcessor.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/TasksCachedProcessor.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/TasksCachedProcessor.java @@ -0,0 +1,160 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; + +/** + * A thread-safe implementation of {@link Computable} interface with an internal + * results cache. + * + * P - task parameter type; R - result type + * + */ +public final class TasksCachedProcessor implements Computable { + + private final ConcurrentMap> cache = new ConcurrentHashMap>(); + private final Computable computable; + private final boolean removeOnCompletion; + + /** + * Creates a new instance of processor. + * + * @param computable a computable that does actual calculations + * @param removeOnCompletion if true, the result will be removed from the + * cache as soon as it gets available. + */ + public TasksCachedProcessor(Computable computable, boolean removeOnCompletion) { + this.computable = computable; + this.removeOnCompletion = removeOnCompletion; + } + + public boolean isResultAvailable(final P arg) { + Future res = cache.get(arg); + + if (res == null) { + return false; + } + + return res.isDone() && !res.isCancelled(); + } + + @Override + public R compute(final P arg) throws NativeExecutionException { + Future f = cache.get(arg); + + if (f == null) { + Callable evaluation = new Callable() { + + @Override + public R call() throws NativeExecutionException { + return computable.compute(arg); + } + }; + + FutureTask ft = new FutureTask(evaluation); + f = cache.putIfAbsent(arg, ft); + + if (f == null) { + f = ft; + ft.run(); + } + } + + try { + return f.get(); + } catch (Throwable th) { + cache.remove(arg, f); + if (th instanceof NativeExecutionException) { + throw (NativeExecutionException) th; + } + if (th.getCause() instanceof NativeExecutionException) { + throw (NativeExecutionException) th.getCause(); + } + throw new NativeExecutionException("", th); + } finally { + if (removeOnCompletion) { + cache.remove(arg, f); + } + } + } + + /** + * Force-remove cached result (if any) for the specified parameter. + * + * @param param the parameter to remove result for. + */ + public void remove(P param) { + Future f = cache.get(param); + + if (f != null && !f.isDone()) { + f.cancel(true); + } + + cache.remove(param); + } + + /** + * Clear an internal results cache. + */ + public void resetCache() { + // Even if some tasks are in progress it's OK just to clear the cache. + // Tasks will not be terminated though... + cache.clear(); + } + + /** + * Non-blocking getter for a future result of already running or done task + * for the specified parameter. + * + * @param param - parameter to get future result for. + * @return A future result of either currently running or already done task. + */ + public Future getFutureResult(P param) { + return cache.get(param); + } +} diff --git a/nativeexecution/src/org/netbeans/modules/nativeexecution/util/URIMatcher.java b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/URIMatcher.java new file mode 100644 --- /dev/null +++ b/nativeexecution/src/org/netbeans/modules/nativeexecution/util/URIMatcher.java @@ -0,0 +1,104 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.net.URI; +import java.util.concurrent.atomic.AtomicReference; +import org.netbeans.modules.nativeexecution.common.ConnectionServiceLookup; +import org.netbeans.modules.nativeexecution.spi.URIIdentifier; +import org.openide.util.Lookup; + +/** + * An utility class for testing URIs for equivalence. + * + *

There is a notion of URIs equivalence. Equivalent URIs could verbally + * differ, but denote the same destination.

+ * + * @author akrasny + */ +public final class URIMatcher { + + private final URI uri; + private final AtomicReference lookupRef = new AtomicReference(); + + /** + * Constructs a new instance of the matcher for the specified URI. + * + * @param uri URI the matcher should be created for. + */ + public URIMatcher(URI uri) { + this.uri = uri; + } + + /** + * Tests weather the provided uri is equivalent to the uri this matcher was + * created for. + * + * @param uri URI to test for equivalence with the uri this matcher was + * created for. + * + * @return {@code true} if URIs are equivalent. + */ + public boolean isIdenticalURI(final URI uri) { + if (this.uri.equals(uri)) { + return true; + } + + Lookup lookup = lookupRef.get(); + + if (lookup == null) { + lookup = ConnectionServiceLookup.getServiceLookup(this.uri); + Lookup old = lookupRef.getAndSet(lookup); + if (old != null) { + lookup = old; + } + } + + for (URIIdentifier identifiers : lookup.lookupAll(URIIdentifier.class)) { + if (identifiers.areIdentical(this.uri, uri)) { + return true; + } + } + + return false; + } +} diff --git a/nativeexecution/test/unit/data/goldenfiles/org/netbeans/modules/nativeexecution/util/MacroMapTest/testPut.pass b/nativeexecution/test/unit/data/goldenfiles/org/netbeans/modules/nativeexecution/util/MacroMapTest/testPut.pass new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/data/goldenfiles/org/netbeans/modules/nativeexecution/util/MacroMapTest/testPut.pass @@ -0,0 +1,31 @@ +---- Map Dump ---- +A = 1 +C = 2 +B = 3 +------------------ +---- Map Dump ---- +A = 5 +c = 2 +b = 3 +C = 4 +------------------ +---- Map Dump ---- +A = 1 +C = 2 +B = 3 +------------------ +---- Map Dump ---- +A = 5 +c = 2 +b = 3 +C = 4 +------------------ +---- Map Dump ---- +A = 1 +C = 2 +B = 3 +------------------ +---- Map Dump ---- +c = 4 +b = 3 +------------------ diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/ConnectionManagerTest.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/ConnectionManagerTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/ConnectionManagerTest.java @@ -0,0 +1,247 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @author Andrew + */ +public class ConnectionManagerTest { + + public ConnectionManagerTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test that:
    + * + *
  • returned connection is never null
  • + * + *
  • same connection is returned in subsequent call
  • + * + *
  • same connection is returned for equivalent URIs
  • + * + *
+ */ + @Test + public void testGetConnection() throws Exception { + final URI uri1 = new URI("test://tester@computer:22"); + final URI uri2 = new URI("test://tester:tester@computer:22"); + + Connection c1, c2; + + c1 = ConnectionManager.getConnection(uri1); + assertNull("ConnectionManager must return NULL if no ACTIVE connection with equivalent URI exists", c1); + + c2 = ConnectionManager.getConnection(uri2); + assertNull("ConnectionManager must return NULL if no ACTIVE connection with equivalent URI exists", c2); + + try { + c1 = ConnectionManager.connect(uri1, null); + assertTrue("At this point connection should be in connected state", c1.isConnected()); + assertNotNull("Returned connection is NEVER null", c1); + assertTrue("Returned connection is connected", c1.isConnected()); + + c2 = ConnectionManager.connect(uri2, null); + assertNotNull("Connection exists => returned connection should be not null", c2); + assertTrue("Returned connection is connected", c2.isConnected()); + + assertEquals("Now connections should be the same", c1, c2); + + ConnectionManager.disconnect(c2); + + assertFalse("connection must be disconnected", c1.isConnected()); + assertFalse("connection must be disconnected", c2.isConnected()); + + } catch (Exception ex) { + assertTrue("Unexpected exception", false); + } + +// c2 = ConnectionManager.connect(uri2, null); +// assertTrue("At this point connection should be in connected state", c2.isConnected()); +// +// assertTrue("Once connection was established, there should be a single Connection object for all equivalent URIs", c1 == c2); +// assertTrue("Once connection was established, there should be a single Connection object for all equivalent URIs", c1 == ConnectionManager.getConnection(uri1)); +// assertTrue("Once connection was established, there should be a single Connection object for all equivalent URIs", c1 == ConnectionManager.getConnection(uri2)); +// +// ConnectionManager.disconnect(c1); +// +// assertFalse("At this point connection should be in disconnected state", c1.isConnected()); +// assertFalse("At this point connection should be in disconnected state", c2.isConnected()); +// +// c2 = ConnectionManager.getConnection(uri2); +// assertNotNull("ConnectionManager must always return not NULL connection", c2); +// +// assertTrue("Even when connection was disconnected - the same object (if still refferenced) should be returned", c1 == c2); +// System.out.println(System.identityHashCode(c1)); +// System.out.println(System.identityHashCode(c2)); +// +// c1 = null; +// c2 = null; +// connection1 = null; +// System.gc(); +// +// c2 = ConnectionManager.getConnection(uri2); +// c2 = ConnectionManager.connect(uri2, null); +// +// System.out.println(System.identityHashCode(c2)); +// System.out.println("should be true: " + c2.isConnected()); +// c2 = null; +// System.gc(); +// c2 = ConnectionManager.getConnection(uri2); +// System.out.println("should be true: " + c2.isConnected()); + } + + /** + * Test of getConnection method, of class ConnectionManager. + */ + @Test + public void testGetLocalConnection() throws Exception { + } +// @Test +// public void testGetConnection() throws Exception { +// final URI uri = new URI("test://tester@computer:22"); +// final AtomicInteger established = new AtomicInteger(0); +// final AtomicInteger closed = new AtomicInteger(0); +// final AtomicInteger lost = new AtomicInteger(0); +// final AtomicInteger other = new AtomicInteger(0); +// +// final ConnectionStateChangeListener listener = new ConnectionStateChangeListener() { +// +// @Override +// public void connectionStateChanged(ConnectionStateChangeEvent ce) { +// switch (ce.getState()) { +// case CONNECTION_ESTABLISHED: +// established.incrementAndGet(); +// break; +// case CONNECTION_CLOSED: +// closed.incrementAndGet(); +// break; +// case CONNECTION_LOST: +// lost.incrementAndGet(); +// break; +// default: +// other.incrementAndGet(); +// } +// } +// }; +// +// +//// ConnectionsRegistryAccessor.addConnectionListener(listener); +// +// JPanel panel = new JPanel(new BorderLayout()); +// JButton button = new JButton("Do it!"); +// button.addActionListener(new ActionListener() { +// +// @Override +// public void actionPerformed(ActionEvent e) { +// try { +// Connection connection = ConnectionManager.getConnection(uri); +// connection.connect(new AuthDataProvider() { +// +// @Override +// public T getProperty(final URI uri, Class clazz, String name, Object... misc) throws InterruptedException { +// DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation("Enter something for: " + uri + " == " + name)); +// Thread.sleep(100000); +// return null; +// } +// }); +// +// if (connection != null && Connection.State.CONNECTED.equals(connection.getState())) { +// assertArrayEquals(new int[]{1, 0, 0, 0}, +// new int[]{established.get(), closed.get(), lost.get(), other.get()}); +// +// } else { +// fail("Should be connected"); +// } +// +// // new TestConnector().disconnect(connection); +// +// // Thread.sleep(3000); +// System.out.println("HERE!"); +// +// Connection connection2 = ConnectionManager.getConnection(uri); +// +// assertTrue(connection == connection2); +// +//// ConnectionsRegistryAccessor.disconnect(connection2); +// +// assertArrayEquals(new int[]{1, 1, 0, 0}, +// new int[]{established.get(), closed.get(), lost.get(), other.get()}); +//// ConnectionsRegistryAccessor.removeConnectionListener(listener); +// } catch (ConnectionCancelledException ex) { +// System.out.println("Cancelled!"); +// } catch (NativeExecutionException ex) { +// Exceptions.printStackTrace(ex); +// } +// } +// }); +// +// panel.add(button); +// DialogDescriptor dd = new DialogDescriptor(panel, "Request"); +// Dialog dialog = DialogDisplayer.getDefault().createDialog(dd); +// dialog.setVisible(true); +// } +} diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/ConnectionTest.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/ConnectionTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/ConnectionTest.java @@ -0,0 +1,172 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.modules.nativeexecution.util.HostInfo; +import org.netbeans.modules.nativeexecution.util.HostInfo.CpuFamily; +import org.netbeans.modules.nativeexecution.util.URIMatcher; +import org.openide.util.Exceptions; + +/** + * + * @author Andrew + */ +public class ConnectionTest { + + public ConnectionTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of connect method, of class Connection. + */ + @Test + public void testConnectionListeners() throws Exception { + try { + final URI uri = new URI("test://tester@computer:22"); + Connection connection = ConnectionManager.getConnection(uri); + assertNull("ConnectionManager must return NULL for not existent connection", connection); + + final AtomicReference lstate = + new AtomicReference(); + + final AtomicInteger count = new AtomicInteger(); + final ConnectionStateChangeListener listener = new ConnectionStateChangeListener() { + + @Override + public void connectionStateChanged(ConnectionStateChangeEvent event) { + URIMatcher m = new URIMatcher(uri); + assertTrue(m.isIdenticalURI(event.getSource())); + lstate.set(event.getState()); + count.incrementAndGet(); + } + }; + + ConnectionManager.addConnectionStateChangeListener(listener, uri); + + // Test that listeners are added weakly + ConnectionManager.addConnectionStateChangeListener(new ConnectionStateChangeListener() { + + @Override + public void connectionStateChanged(ConnectionStateChangeEvent event) { + assertTrue("This listener should not be called!", false); + } + }, uri); + + System.gc(); + + assertEquals(null, lstate.get()); + + connection = ConnectionManager.connect(uri, null); + + HostInfo hostInfo = HostInfo.getFor(connection); + assertNotNull("HostInfo is never null", hostInfo); + + assertEquals("After connection establishment must receive a CONNECTION_ESTABLISHED event", + ConnectionStateChangeEvent.State.CONNECTION_ESTABLISHED, lstate.get()); + + assertTrue("After connection establishment must be in CONNECTED state", + connection.isConnected()); + assertEquals("Connected connection must have KNOWN host info", CpuFamily.X86, hostInfo.getCpuFamily()); + + ConnectionManager.disconnect(connection); + + hostInfo = HostInfo.getFor(connection); + assertEquals("When disconnected with an API call, must get CONNECTION_CLOSED event", + ConnectionStateChangeEvent.State.CONNECTION_CLOSED, lstate.get()); + assertFalse("When disconnected with an API call, must be in DISCONNECTED state", + connection.isConnected()); + assertEquals("Connected connection must have KNOWN host info", CpuFamily.X86, hostInfo.getCpuFamily()); + + ConnectionManager.connect(uri, null); + assertEquals("On reconnect must get CONNECTION_ESTABLISHED event", + ConnectionStateChangeEvent.State.CONNECTION_ESTABLISHED, lstate.get()); + assertTrue("On reconnect must appear in CONNECTED state", + connection.isConnected()); + + // Emulate broken connection (disconnected not via API) + TestConnector.lastTestConnection.get().disconnect(); + // Some time may be required to see that connection is broken ... + Thread.sleep(1100); + + hostInfo = HostInfo.getFor(connection); + assertEquals("When disconnected (non-API) must get CONNECTION_LOST event", + ConnectionStateChangeEvent.State.CONNECTION_LOST, lstate.get()); + assertFalse("When disconnected (non-API) must be in DISCONNECTED state", + connection.isConnected()); + assertEquals("Connected connection must have KNOWN host info", CpuFamily.X86, hostInfo.getCpuFamily()); + + ConnectionManager.disconnect(connection); + assertEquals("Total notifications. API call to diconnect must not send notification at this point", + 4, count.get()); + } catch (NativeExecutionException ex) { + Exceptions.printStackTrace(ex); + } catch (URISyntaxException ex) { + Exceptions.printStackTrace(ex); + } + } +} diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/TestConnectionIdentifier.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/TestConnectionIdentifier.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/TestConnectionIdentifier.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; +import org.netbeans.modules.nativeexecution.spi.URIIdentifier; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.nativeexecution.spi.URIIdentifier.class, path = "ConnectionService/test", position = 10) +public class TestConnectionIdentifier implements URIIdentifier { + + @Override + public boolean areIdentical(URI uri1, URI uri2) { + if (!(uri1.getScheme().equals(uri2.getScheme()))) { + return false; + } + + String host1 = uri1.getHost(); + String host2 = uri2.getHost(); + + if (host1 == null || host2 == null) { + throw new NullPointerException("Test connection URI must define host"); + } + + String user1 = uri1.getUserInfo(); + String user2 = uri2.getUserInfo(); + if (user1 == null || user2 == null) { + throw new NullPointerException("Test connection URI must define user"); + } + + int cpos; + if ((cpos = user1.indexOf(':')) > 0) { + user1 = user1.substring(0, cpos); + } + if ((cpos = user2.indexOf(':')) > 0) { + user2 = user2.substring(0, cpos); + } + + return host1.equals(host2) && user1.equals(user2); + } +} diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/TestConnector.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/TestConnector.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/TestConnector.java @@ -0,0 +1,159 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import org.netbeans.modules.nativeexecution.spi.AuthDataProvider; +import org.netbeans.modules.nativeexecution.spi.ConnectionImplementation; +import org.netbeans.modules.nativeexecution.spi.ConnectorImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessImplementation; +import org.netbeans.modules.nativeexecution.spi.process.NativeProcessParams; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfo; +import org.netbeans.modules.nativeexecution.spi.support.ConnectionInfoProvider; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ServiceProvider; + +@ServiceProvider(service = org.netbeans.modules.nativeexecution.spi.ConnectorImplementation.class, path = "ConnectionService/test", position = 10) +public final class TestConnector implements ConnectorImplementation { + + public static final AtomicReference lastTestConnection = new AtomicReference(); + + @Override + public ConnectionImplementation connectTo(URI uri, AuthDataProvider authDataProvider) throws InterruptedException, NativeExecutionException { + authDataProvider.getProperty(uri, String.class, "test"); + TestConnection cimpl = new TestConnection(uri); + lastTestConnection.set(cimpl); + return cimpl; + } + + public static class TestConnection implements ConnectionImplementation { + + private boolean closed = false; + private final String name; + private final URI uri; + + private TestConnection(URI uri) { + this.name = "Test connection to " + uri.toString(); + this.uri = uri; + } + + @Override + public void disconnect() { + closed = true; + } + + @Override + public String toString() { + return name; + } + + @Override + public NativeProcessCreator newProcessBuilderImpl() throws NativeExecutionException { + return FakeProcessCreator.instance; + } + + @Override + public boolean isConnected() { + return !closed; + } + + @Override + public Lookup getLookup() { + return Lookups.fixed(new ConnectionInfoProvider() { + + @Override + public ConnectionInfo getConnectionInfo(Connection connection) throws NativeExecutionException { + return new ConnectionInfo() { + + @Override + public EnvironmentMap getEnvironmentMap() { + EnvironmentMap env = EnvironmentMap.createCasePreserving(':'); + env.put("TEST_ENV", "flag"); + return env; + } + + @Override + public Map getProperties() { + Map result = new HashMap(); + result.put("CPUFAMILY", "X86"); + return result; + } + }; + } + }); + } + + @Override + public URI getURI() { + return uri; + } + } + + public static class FakeProcessCreator implements NativeProcessCreator { + + public static final FakeProcessCreator instance = new FakeProcessCreator(); + public NativeProcessParams params; + private static final Map> props = new HashMap>(); + + static { + props.put("str", CharSequence.class); + props.put("bool", Boolean.class); + props.put("byteArr", byte[].class); + } + + @Override + public Map> getSupportedProperties() { + return props; + } + + @Override + public NativeProcessImplementation createAndStart(NativeProcessParams params) throws NativeExecutionException { + this.params = params; + throw new NativeExecutionException("Not supported"); + } + } +} \ No newline at end of file diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/process/NativeProcessParamsTest.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/process/NativeProcessParamsTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/api/process/NativeProcessParamsTest.java @@ -0,0 +1,147 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.api.process; + +import java.net.URI; +import java.util.Map; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.TestConnector; +import org.netbeans.modules.nativeexecution.api.TestConnector.FakeProcessCreator; +import org.openide.util.Exceptions; + +/** + * + * @author Andrew + */ +public class NativeProcessParamsTest { + + public NativeProcessParamsTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + @Test + public void testGetProperty() { + try { + Connection connection = ConnectionManager.connect(new URI("test://user@testhost")); + String str = "test"; + NativeProcessBuilder npb = connection.newProcessBuilder(); + + try { + npb.setProperty("str", new Integer(1)); + fail("Exception expected"); + } catch (IllegalArgumentException ex) { + } + + npb.setProperty("str", str); + try { + npb.call(); + } catch (Exception e) { + } + String result = TestConnector.FakeProcessCreator.instance.params.getProperty(String.class, "str"); + assertEquals(str, result); + + CharSequence cs = TestConnector.FakeProcessCreator.instance.params.getProperty(CharSequence.class, "str"); + assertEquals(str, cs.toString()); + + Integer i = TestConnector.FakeProcessCreator.instance.params.getProperty(Integer.class, "str"); + assertNull(i); + + try { + npb.setProperty("other", new Integer(1)); + fail("Exception expected"); + } catch (IllegalArgumentException ex) { + } + + try { + npb.call(); + } catch (Exception e) { + } + + assertNull(TestConnector.FakeProcessCreator.instance.params.getProperty(String.class, "other")); + + npb.setProperty("bool", false); + npb.setProperty("bool", Boolean.TRUE); + + npb.setProperty("byteArr", new byte[]{1, 2, 3}); + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + fail(); + } + } + + @Test + public void testGetSupportedOptions() { + try { + FakeProcessCreator npb = new FakeProcessCreator(); + Map> sp = npb.getSupportedProperties(); + assertTrue(sp != null && sp.size() == 3); + assertEquals(CharSequence.class, sp.get("str")); + assertEquals(Boolean.class, sp.get("bool")); + assertEquals(byte[].class, sp.get("byteArr")); + } catch (Exception ex) { + Exceptions.printStackTrace(ex); + fail(); + } + } +} diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/common/ConnectionsRegistryTest.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/common/ConnectionsRegistryTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/common/ConnectionsRegistryTest.java @@ -0,0 +1,82 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.common; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.BeforeClass; + +/** + * + * @author Andrew + */ +public class ConnectionsRegistryTest { + + public ConnectionsRegistryTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of fromFileName method, of class ConnectionsRegistry. + */ + @Test + public void test() { + + } +} diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/EnvironmentMapTest.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/EnvironmentMapTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/EnvironmentMapTest.java @@ -0,0 +1,115 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.util.HashMap; +import java.util.Map; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.modules.nativeexecution.api.EnvironmentMap; + +/** + * + * @author Andrew + */ +public class EnvironmentMapTest { + + public EnvironmentMapTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of prependPath method, of class EnvironmentMap. + */ + @Test + public void testPrependPathVariable() { + Map special = new HashMap(); + special.put("OS", "Win"); + + EnvironmentMap map = EnvironmentMap.createCasePreserving(';'); + map.setDictionary(special); + map.put("Path", ""); + map.put("PATH", "${PATH};${OS}"); + map.appendPath("path", ""); + + assertEquals(1, map.size()); + assertEquals(";Win;", map.get("path")); + String name = map.entrySet().iterator().next().getKey(); + + // First time case was exactly Path - it should be preserved + assertEquals("Path", name); + + + special = new HashMap(); + special.put("OS", "UNIX"); + + map = EnvironmentMap.createCaseSensitive(':'); + map.setDictionary(special); + map.put("Path", ""); + map.put("PATH", "${PATH}:${OS}"); + map.appendPath("path", ""); + + assertEquals(3, map.entrySet().size()); + assertEquals("", map.get("path")); + assertEquals("${PATH}:UNIX", map.get("PATH")); + assertEquals("", map.get("Path")); + } +} diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/HostInfoTest.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/HostInfoTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/HostInfoTest.java @@ -0,0 +1,100 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.BeforeClass; +import org.netbeans.modules.nativeexecution.api.Connection; +import org.netbeans.modules.nativeexecution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.api.process.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.util.ProcessUtils.ExitStatus; +import org.openide.util.Exceptions; +import org.openide.util.Utilities; + +/** + * + * @author akrasny + */ +public class HostInfoTest { + + public HostInfoTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of getFor method, of class HostInfo. + */ + @Test + public void testLocal() { + Connection local = ConnectionManager.getLocalConnection(); + HostInfo info = HostInfo.getFor(local); + System.out.println(info.getOS().getName()); + try { + NativeProcessBuilder pb = local.newProcessBuilder(); + pb.setCommand("/bin/uname", "-a"); + ExitStatus res = ProcessUtils.execute(pb); + System.out.println(res.output); + } catch (NativeExecutionException ex) { + Exceptions.printStackTrace(ex); + } + + } +} diff --git a/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/MacroMapTest.java b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/MacroMapTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/util/MacroMapTest.java @@ -0,0 +1,200 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2012 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2012 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.util; + +import java.text.Collator; +import java.util.Locale; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; + +/** + * + * @author akrasny + */ +public class MacroMapTest extends NbTestCase { + + public MacroMapTest(String name) { + super(name); + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + @Override + public void setUp() { + } + + @After + @Override + public void tearDown() { + } + + @Test + public void testX() { + final RequestProcessor RP = new RequestProcessor("A", 10); + RP.post(new Runnable() { + + @Override + public void run() { + Thread.currentThread().setName("Invoke thread"); + doit(RP); + } + }).waitFinished(); + System.out.println("ALL done"); + + } + + private void doit(RequestProcessor RP) { + Future fres = RP.submit(new Callable() { + + @Override + public Void call() throws Exception { + Thread.currentThread().setName("Process's thread"); + try { + Thread.sleep(100); + } finally { + System.out.println("Process is DONE"); + } + + return null; + } + }); + + try { + fres.get(); + System.out.println("HERE is NO exception"); + } catch (InterruptedException ex) { + Thread.interrupted(); + System.out.println("INVOKE THREAD WAS INTERRUPTED"); + fres.cancel(true); + try { + fres.get(1, TimeUnit.SECONDS); + } catch (TimeoutException ex1) { + System.out.println("FAILED TO STOP!!!!"); + Exceptions.printStackTrace(ex1); + } catch (InterruptedException ex1) { + Thread.interrupted(); + System.out.println("INTERRUPTED AGAIN!!!!"); + Exceptions.printStackTrace(ex1); + } catch (ExecutionException ex1) { + System.out.println("STOPPED!!!!"); + Exceptions.printStackTrace(ex1); + } + } catch (ExecutionException ex) { + System.out.println("PROCESS's THREAD WAS INTERRUPTED"); + Exceptions.printStackTrace(ex); + } + + } + + /** + * Test of put method, of class MacroMap. + */ +// @Test + public void _testPut() { + MacroMap map; + + map = new MacroMap(); + map.put("A", "1"); + map.put("C", "2"); + map.put("B", "3"); + map.dump(getRef()); + + map = new MacroMap(); + map.put("A", "1"); + map.put("c", "2"); + map.put("b", "3"); + map.put("C", "4"); + map.put("A", "5"); + map.dump(getRef()); + + Collator col = Collator.getInstance(Locale.US); + map = new MacroMap(col); + map.put("A", "1"); + map.put("C", "2"); + map.put("B", "3"); + map.dump(getRef()); + + map = new MacroMap(col); + map.put("A", "1"); + map.put("c", "2"); + map.put("b", "3"); + map.put("C", "4"); + map.put("A", "5"); + map.dump(getRef()); + + col.setStrength(Collator.PRIMARY); + map = new MacroMap(col); + map.put("A", "1"); + map.put("C", "2"); + map.put("B", "3"); + map.dump(getRef()); + + map = new MacroMap(col); + map.put("A", "1"); + map.put("c", "2"); + map.put("b", "3"); + map.put("C", "4"); + map.put("A", "5"); + map.remove("a"); + map.dump(getRef()); + + compareReferenceFiles(); + } +} diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -334,6 +334,8 @@ libs.xerces,\ localhistory,\ mercurial,\ + nativeexecution,\ + nativeexecution.impl,\ o.apache.commons.codec,\ o.apache.commons.httpclient,\ o.apache.commons.io,\ diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml --- a/nbbuild/javadoctools/links.xml +++ b/nbbuild/javadoctools/links.xml @@ -216,4 +216,7 @@ + + + diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml --- a/nbbuild/javadoctools/properties.xml +++ b/nbbuild/javadoctools/properties.xml @@ -214,4 +214,7 @@ + + + diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml --- a/nbbuild/javadoctools/replaces.xml +++ b/nbbuild/javadoctools/replaces.xml @@ -214,4 +214,7 @@ + + +