diff --git a/cnd.api.remote/nbproject/project.xml b/cnd.api.remote/nbproject/project.xml --- a/cnd.api.remote/nbproject/project.xml +++ b/cnd.api.remote/nbproject/project.xml @@ -38,6 +38,14 @@ + org.netbeans.modules.cnd.execution + + + + 1.0 + + + org.netbeans.modules.projectapi diff --git a/cnd.debugger.common2/nbproject/project.xml b/cnd.debugger.common2/nbproject/project.xml --- a/cnd.debugger.common2/nbproject/project.xml +++ b/cnd.debugger.common2/nbproject/project.xml @@ -154,6 +154,14 @@ + org.netbeans.modules.cnd.execution + + + + 1.0 + + + org.netbeans.modules.options.api diff --git a/cnd.execution/arch.xml b/cnd.execution/arch.xml new file mode 100644 --- /dev/null +++ b/cnd.execution/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/cnd.execution/build.xml b/cnd.execution/build.xml new file mode 100755 --- /dev/null +++ b/cnd.execution/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.nativeexecution + + diff --git a/cnd.execution/manifest.mf b/cnd.execution/manifest.mf new file mode 100755 --- /dev/null +++ b/cnd.execution/manifest.mf @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 +AutoUpdate-Show-In-Client: false +OpenIDE-Module: org.netbeans.modules.cnd.execution +OpenIDE-Module-Implementation-Version: 1 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/cnd/execution/resources/Bundle.properties +OpenIDE-Module-Needs: ConnectionService.localhost +OpenIDE-Module-Provides: org.netbeans.modules.dlight.spi.terminal.ConnectionProvider + diff --git a/cnd.execution/nbproject/project.properties b/cnd.execution/nbproject/project.properties new file mode 100755 --- /dev/null +++ b/cnd.execution/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/cnd.execution/nbproject/project.xml b/cnd.execution/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/cnd.execution/nbproject/project.xml @@ -0,0 +1,108 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.cnd.execution + + + org.netbeans.api.progress + + + + 1 + 1.28 + + + + org.netbeans.modules.dlight.terminal + + + + 1.14 + + + + org.netbeans.modules.extexecution + + + + 2 + 1.36 + + + + 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.nbjunit + + + + + org.netbeans.modules.progress.ui + + + + + org.openide.dialogs + + + + org.openide.util + + + + + org.openide.util.lookup + + + + + + org.netbeans.modules.cnd.execution.api + org.netbeans.modules.cnd.execution.api.config + org.netbeans.modules.cnd.execution.api.process + org.netbeans.modules.cnd.execution.spi + org.netbeans.modules.cnd.execution.spi.support + org.netbeans.modules.cnd.execution.util + + + + bitness + + + diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/ExecutionLogger.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/ExecutionLogger.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution; + +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", "SEVERE").toUpperCase(); // NOI18N + Level level = Level.SEVERE; + + 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.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/cnd.execution/src/org/netbeans/modules/cnd/execution/NativeProcessFactory.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/NativeProcessFactory.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/NativeProcessFactory.java @@ -0,0 +1,127 @@ +/* + * 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.cnd.execution; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import org.netbeans.api.extexecution.Environment; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.spi.extexecution.EnvironmentFactory; +import org.netbeans.spi.extexecution.EnvironmentImplementation; +import org.netbeans.spi.extexecution.ProcessBuilderImplementation2; +import org.netbeans.spi.extexecution.ProcessParameters; +import org.openide.util.Lookup; + +/** + * + * @author Petr Hejl + */ +public class NativeProcessFactory implements ProcessBuilderImplementation2 { + + private final NativeProcessBuilder builder; + + private final Environment environment; + + public NativeProcessFactory(NativeProcessBuilder builder) { + this.builder = builder; + this.environment = EnvironmentFactory.createEnvironment(new NativeEnvironment()); + } + + @Override + public Environment getEnvironment() { + return environment; + } + + @Override + public Lookup getLookup() { + return builder.getLookup(); + } + + @Override + public Process createProcess(ProcessParameters parameters) throws IOException { + builder.setCommand(parameters.getExecutable(), + parameters.getArguments().toArray(new String[parameters.getArguments().size()])); + builder.setWorkingDirectory(parameters.getWorkingDirectory()); + builder.redirectErrorStream(parameters.isRedirectErrorStream()); + + try { + return builder.call(); + } catch (NativeExecutionException ex) { + throw new IOException(ex); + } + } + + private class NativeEnvironment implements EnvironmentImplementation { + + @Override + public String getVariable(String name) { + return builder.getEnvironmentMap().get(name); + } + + @Override + public void appendPath(String name, String value) { + builder.getEnvironmentMap().appendPath(name, value, false); + } + + @Override + public void prependPath(String name, String value) { + builder.getEnvironmentMap().prependPath(name, value, false); + } + + @Override + public void setVariable(String name, String value) { + builder.getEnvironmentMap().put(name, value, false); + } + + @Override + public void removeVariable(String name) { + builder.getEnvironmentMap().remove(name); + } + + @Override + public Map values() { + return builder.getEnvironmentMap().toMap(); + } + } +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/access/ConnectionAccessor.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/access/ConnectionAccessor.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.access; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/access/EnvironmentMapAccessor.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/access/EnvironmentMapAccessor.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/access/EnvironmentMapAccessor.java @@ -0,0 +1,88 @@ +/* + * 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.cnd.execution.access; + +import java.util.Map; +import org.netbeans.modules.cnd.execution.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; + public abstract Map getModifiedVars(EnvironmentMap environmentMap); + + public abstract EnvironmentMap createBasedOn(EnvironmentMap map); + + public abstract EnvironmentMap createCasePreserving(char pathSeparator); + + public abstract EnvironmentMap createCaseSensitive(char pathSeparator); +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/access/NativeProcessAccessor.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/access/NativeProcessAccessor.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.access; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.common.NativeProcess; +import org.netbeans.modules.cnd.execution.spi.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/cnd.execution/src/org/netbeans/modules/cnd/execution/access/NativeProcessBuilderFactory.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/access/NativeProcessBuilderFactory.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.access; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.modules.cnd.execution.spi.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/cnd.execution/src/org/netbeans/modules/cnd/execution/access/NativeProcessParamsFactory.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/access/NativeProcessParamsFactory.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/access/NativeProcessParamsFactory.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.cnd.execution.access; + +import java.util.List; +import java.util.Map; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.openide.util.Lookup; + +/** + * + * @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, + List command, String shell, String shellScript, boolean redirectError, + String wdir, Lookup lookup); +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/api/BrokenConnectionException.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/BrokenConnectionException.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/Bundle.properties b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/Bundle.properties new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/Connection.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/Connection.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/Connection.java @@ -0,0 +1,207 @@ +/* + * 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.cnd.execution.api; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.ConnectException; +import java.net.NoRouteToHostException; +import java.net.URI; +import org.netbeans.modules.cnd.execution.NativeProcessFactory; +import org.netbeans.modules.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.access.NativeProcessBuilderFactory; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.ConnectorImplementation; +import org.netbeans.modules.cnd.execution.spi.URIIdentifier; +import org.netbeans.spi.extexecution.ProcessBuilderFactory; +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 implements org.netbeans.api.extexecution.ProcessBuilder.Provider { + + 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 newNativeProcessBuilder() throws NativeExecutionException { + synchronized (implLock) { + if (!isConnected()) { + throw new BrokenConnectionException(impl.getURI()); + } + + return NativeProcessBuilderFactory.getDefault().newProcessBuilder(impl.newProcessBuilderImpl(), this); + } + } + + /** + * Creates a new {@link org.netbeans.api.extexecution.ProcessBuilder} to be used for starting + * processes within this connection. + *

+ * The returned process builder is not thread safe. + * + * @return new {@link org.netbeans.api.extexecution.ProcessBuilder}. + * @throws ConnectException if the connection object is not actually connected + * @throws InterruptedIOException if the user interrupted or canceled the + * communication + * @throws NoRouteToHostException if there is problem with connection to host + * @throws IOException if some problem occurred + */ + @Override + public org.netbeans.api.extexecution.ProcessBuilder getProcessBuilder() + throws ConnectException, InterruptedIOException, NoRouteToHostException, IOException { + synchronized (implLock) { + if (!isConnected()) { + throw new ConnectException(impl.getURI().toString()); + } + + return ProcessBuilderFactory.createProcessBuilder( + new NativeProcessFactory(NativeProcessBuilderFactory.getDefault().newProcessBuilder(impl.newProcessBuilderImpl(), this)), + getDisplayName()); + } + } + + /** + * 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(); + } + } + + public final boolean isLocal() { + return this.equals(ConnectionManager.getLocalConnection()); + } + + public String getDisplayName() { + return 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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionException.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionException.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionManager.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionManager.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionManager.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.cnd.execution.api; + +import java.io.IOException; +import java.net.URI; +import org.netbeans.api.extexecution.ProcessBuilder; +import org.netbeans.modules.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.api.ConnectionStateChangeEvent.State; +import org.netbeans.modules.cnd.execution.common.ConnectionListeners; +import org.netbeans.modules.cnd.execution.common.ConnectionsPool; +import org.netbeans.modules.cnd.execution.common.Connector; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.URIIdentifier; +import org.netbeans.modules.dlight.spi.terminal.ConnectionProvider; +import org.openide.util.Exceptions; +import org.openide.util.lookup.ServiceProvider; + +/** + * 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 IOException { + 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 IOException { + 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); + } + + @ServiceProvider(service=ConnectionProvider.class) + public static class NativeConnectionProvider implements ConnectionProvider { + + @Override + public ProcessBuilder.Provider getConnection(URI uri) throws IOException { + Connection connection = ConnectionManager.getConnection(uri); + if (connection == null) { + connection = ConnectionManager.connect(uri); + } + return connection; + } + + @Override + public URI getLocal() { + return ConnectionManager.getLocalConnection().getURI(); + } + + } +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionStateChangeEvent.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionStateChangeEvent.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionStateChangeListener.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/ConnectionStateChangeListener.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/EnvironmentMap.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/EnvironmentMap.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/EnvironmentMap.java @@ -0,0 +1,461 @@ +/* + * 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.cnd.execution.api; + +import java.io.PrintStream; +import java.text.Collator; +import java.text.ParseException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import org.netbeans.modules.cnd.execution.access.EnvironmentMapAccessor; +import org.netbeans.modules.cnd.execution.util.MacroExpander; + +/** + * 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 final class EnvironmentMap implements Cloneable { + + private final Comparator comparator; + private final EnvironmentMap baseMap; + private final LinkedHashMap ownMap = new LinkedHashMap(); + private final HashSet removedKeys = new HashSet(); + private final char pathSeparator; + + static { + EnvironmentMapAccessor.setDefault(new Accessor()); + } + + /** + * Creates a new map with a specified keys comparator. + * + * @param comparator the comparator to be used in keys comparisons. + */ + private EnvironmentMap(EnvironmentMap baseMap) { + this.baseMap = baseMap; + this.comparator = baseMap.comparator; + this.pathSeparator = baseMap.pathSeparator; + } + + private EnvironmentMap(Comparator comparator, char pathSeparator) { + this.baseMap = null; + this.comparator = comparator; + this.pathSeparator = pathSeparator; + } + + /** + * Adds a new key-value pair to the map. + * + *

The {@link #put(String, String)} method works as follows: + * + * + * EnvironmentMap m = new EnvironmentMap() + * 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(final String key, final String value) { + return put(key, value, true); + } + + // XXX reduce visibility of this + public final String put(final String key, final String value, boolean expand) { + if (value == null) { + throw new NullPointerException("Attempt to set a null value"); // NOI18N + } + String result = value; + + + String fixedKey = fixKey(key); + + if (expand) { + try { + result = MacroExpander.expandMacros(value, toMap()); + } catch (ParseException ex) { + } + } + removedKeys.remove(fixedKey); + return ownMap.put(fixedKey, result); + + } + + protected Comparator getComparator() { + return comparator; + } + + /** + * 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(final String key) { + String k = fixKey(key); + return getImpl(k); + } + + /** + * 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) { + appendPath(name, path, true); + } + + // XXX reduce visibility of this + public void appendPath(String name, String path, boolean expand) { + if (path == null) { + return; + } + + String oldpath = get(name); + String newPath = (oldpath == null ? "" : oldpath + (pathSeparator)) + path; // NOI18N + put(name, reducePath(newPath), expand); + } + + /** + * 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) { + prependPath(name, path, true); + } + + // XXX reduce visibility of this + public void prependPath(String name, String path, boolean expand) { + if (path == null) { + return; + } + + String oldpath = get(name); + String newPath = path + (oldpath == null ? "" : (pathSeparator) + oldpath); // NOI18N + put(name, reducePath(newPath), expand); + } + + /** + * 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(final String key) { + String k = fixKey(key); + String prevValue = getImpl(k); + ownMap.remove(k); + removedKeys.add(k); + return prevValue; + } + + /** + * Returns an entry set of the map in the same order as they have been + * added. + * + * @return the entry set. + */ + public final Set> entrySet() { + Set> result = new LinkedHashSet>(); + if (baseMap != null) { + for (Entry entry : baseMap.entrySet()) { + String k = entry.getKey(); + if (removedKeys.contains(k)) { + continue; + } + if (ownMap.containsKey(k)) { + continue; + } + result.add(entry); + } + } + + result.addAll(ownMap.entrySet()); + return result; + } + + /** + * Returns the number of key-value mappings in this map. + * + * @return the number of key-value mappings in this map + */ + public final int size() { + int baseSize = 0; + if (baseMap != null) { + for (Entry entry : baseMap.entrySet()) { + String k = entry.getKey(); + if (removedKeys.contains(k)) { + continue; + } + if (ownMap.containsKey(k)) { + continue; + } + baseSize++; + } + } + + return baseSize + ownMap.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 size() == 0; + } + + /** + * Removes all entries from this map. + * + * The map becomes empty after this call returns. + */ + public void clear() { + ownMap.clear(); + removedKeys.clear(); + if (baseMap != null) { + for (Entry entry : baseMap.entrySet()) { + removedKeys.add(entry.getKey()); + } + } + } + + /** + * Returns an unmodifiable copy of this map as a {@code Map} + * collection. + * + * @return unmodifiable copy + */ + public Map toMap() { + TreeMap result = comparator == null + ? new TreeMap() + : new TreeMap(comparator); + + for (Entry entry : entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + + return Collections.unmodifiableMap(result); + } + + private String fixKey(final String key) { + if (comparator == null) { + return key; + } + + for (String k : ownMap.keySet()) { + if (comparator.compare(k, key) == 0) { + return k; + } + } + + // key was not found in the ownMap + // perhaps it is in the base one? + if (baseMap != null) { + for (Entry entry : baseMap.entrySet()) { + String k = entry.getKey(); + if (comparator.compare(k, key) == 0) { + return k; + } + } + } + + return key; + } + + private String getImpl(final String fixedKey) { + if (removedKeys.contains(fixedKey)) { + return null; + } + String result = ownMap.get(fixedKey); + if (result != null) { + return result; + } + if (baseMap != null) { + return baseMap.getImpl(fixedKey); + } + return null; + } + + 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); + } + + public void putAll(Set> entrySet) { + for (Entry entry : entrySet) { + put(entry.getKey(), entry.getValue()); + } + } + + private static class Accessor extends EnvironmentMapAccessor { + + public Accessor() { + } + + @Override + public Map getModifiedVars(EnvironmentMap environmentMap) { + return Collections.unmodifiableMap(environmentMap.ownMap); + } + + @Override + public EnvironmentMap createBasedOn(EnvironmentMap map) { + return new EnvironmentMap(map); + } + + @Override + public EnvironmentMap createCasePreserving(char pathSeparator) { + Collator c = Collator.getInstance(Locale.US); + c.setStrength(Collator.PRIMARY); + return new EnvironmentMap(c, pathSeparator); + } + + @Override + public EnvironmentMap createCaseSensitive(char pathSeparator) { + return new EnvironmentMap(null, pathSeparator); + } + } +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/api/NativeExecutionException.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/NativeExecutionException.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/NativeExecutionException.java @@ -0,0 +1,87 @@ +/* + * 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.cnd.execution.api; + +import java.io.IOException; + +/** + * The root exception of all execution-related exceptions. + * + * This exception is thrown if any execution problem occurs. + * + * @author akrasny + */ +public class NativeExecutionException extends IOException { + + 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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/NativeProcessBuilder.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/NativeProcessBuilder.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/NativeProcessBuilder.java @@ -0,0 +1,331 @@ +/* + * 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.cnd.execution.api; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; +import org.netbeans.modules.cnd.execution.api.process.ProcessState.State; +import org.netbeans.modules.cnd.execution.access.EnvironmentMapAccessor; +import org.netbeans.modules.cnd.execution.access.NativeProcessBuilderFactory; +import org.netbeans.modules.cnd.execution.access.NativeProcessParamsFactory; +import org.netbeans.modules.cnd.execution.api.config.StateListenerConfiguration; +import org.netbeans.modules.cnd.execution.api.config.StateListenerConfiguration.ProcessStateChangeEvent; +import org.netbeans.modules.cnd.execution.api.config.StateListenerConfiguration.ProcessStateChangeListener; +import org.netbeans.modules.cnd.execution.common.ConnectionsInfo; +import org.netbeans.modules.cnd.execution.common.NativeProcess; +import org.netbeans.modules.cnd.execution.common.ProcessStateChangeNotifier; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.util.HostInfo; +import org.netbeans.modules.dlight.spi.terminal.ShellConfiguration; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; + +/** + * 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, Lookup.Provider { + + private final NativeProcessCreator creator; + private final Connection connection; + private final List listeners = new ArrayList(); + private final EnvironmentMap environmentMap; + private final StateListenerConfiguration stateConfig = new StateListenerConfiguration(this); + private final ShellConfiguration shellConfig = new ShellConfiguration() { + @Override + public String getShell() { + return HostInfo.getFor(connection).getShell(); + } + }; + + 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) { + this.creator = creator; + this.connection = connection; + + EnvironmentMapAccessor mapAccess = EnvironmentMapAccessor.getDefault(); + EnvironmentMap envMap = HostInfo.getEnvironmentMap(connection); + environmentMap = envMap == null ? mapAccess.createCaseSensitive(':') : mapAccess.createBasedOn(envMap); + } + + /** + * 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; + } + + public NativeProcessBuilder removeProcessListener(ProcessStateChangeListener listener) { + listeners.remove(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; + } + + @Override + public Lookup getLookup() { + return new ProxyLookup(Lookups.fixed(stateConfig, shellConfig), creator.getLookup()); + } + + /** + * 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 Process 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(), + EnvironmentMapAccessor.getDefault().getModifiedVars(environmentMap), + command, + shell, + shellScript, + redirectError, + wdir, + creator.getLookup()); + + 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/cnd.execution/src/org/netbeans/modules/cnd/execution/api/config/StateListenerConfiguration.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/config/StateListenerConfiguration.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/config/StateListenerConfiguration.java @@ -0,0 +1,169 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.modules.cnd.execution.api.config; + +import java.util.EventListener; +import java.util.EventObject; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.modules.cnd.execution.api.process.ProcessState; + +/** + * + * @author Petr Hejl + */ +public final class StateListenerConfiguration { + + private final NativeProcessBuilder npb; + + public StateListenerConfiguration(NativeProcessBuilder npb) { + this.npb = npb; + } + + + public void addProcessListener(ProcessStateChangeListener listener) { + this.npb.addProcessListener(listener); + } + + public void removeProcessListener(ProcessStateChangeListener listener) { + this.npb.removeProcessListener(listener); + } + + /** + * 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); + } + + /** + * ProcessStateChangeEvent is used to notify interested parties that state + * of the source's process has changed. + * + * @author akrasny + */ + public static final class ProcessStateChangeEvent extends EventObject { + + private static final long serialVersionUID = 1L; + private final ProcessState.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(Process process, ProcessState.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(Process process, ProcessState.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 ProcessState.State getState() { + return state; + } + + /** + * Returns the source {@link Process} for this event. + * + * @return the event source + */ + @Override + public Process getSource() { + return (Process) super.getSource(); + } + + /** + * Returns the timestamp of when this event occurred. + * + * @return this event's timestamp. + */ + public long getEventNanoTime() { + return ts; + } + } +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/api/process/ProcessState.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/process/ProcessState.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/process/ProcessState.java @@ -0,0 +1,107 @@ +/* + * 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.cnd.execution.api.process; + +import org.openide.util.Lookup; + +/** + * + * @author Petr Hejl + */ +public abstract class ProcessState { + + private static ProcessState find(Process process) { + if (process instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) process; + return p.getLookup().lookup(ProcessState.class); + } + return null; + } + + public static boolean isSupported(Process process) { + return find(process) != null; + } + + public static State getState(Process process) { + ProcessState processState = find(process); + if (processState != null) { + return processState.getState(); + } + return null; + } + + protected abstract State getState(); + + /** + * Enumerates all possible states of a {@link Process}. + */ + 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 is done, but exit status is not available yet. + */ + FINISHING, + /** + * Native process exited. + */ + FINISHED, + /** + * Native process submission failed due to some exception. + */ + ERROR + } +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/api/process/package-info.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/process/package-info.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/api/process/package-info.java @@ -0,0 +1,51 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-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]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + * + * 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. + */ + +/** + * The API supporting enhanced functionality for + * a {@link java.lang.Process}. Could be moved to org.netbeans.api.extexecution.process + * in future. + */ +package org.netbeans.modules.cnd.execution.api.process; + diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/common/Bundle.properties b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/Bundle.properties new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionListeners.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionListeners.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.cnd.execution.api.ConnectionStateChangeEvent; +import org.netbeans.modules.cnd.execution.api.ConnectionStateChangeEvent.State; +import org.netbeans.modules.cnd.execution.api.ConnectionStateChangeListener; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionServiceLookup.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionServiceLookup.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionsInfo.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionsInfo.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionsInfo.java @@ -0,0 +1,146 @@ +/* + * 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.cnd.execution.common; + +import java.net.URI; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.modules.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.access.EnvironmentMapAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.EnvironmentMap; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfoProvider; +import org.netbeans.modules.cnd.execution.util.HostInfo; +import org.netbeans.modules.cnd.execution.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) { + Map map = data.getEnvironmentMap(); + if (map != null) { + info.env.putAll(data.getEnvironmentMap()); + } + + Map props = data.getProperties(); + if (props != null) { + info.properties.putAll(props); + } + } + } catch (NativeExecutionException ex) { + } + } + + setConnectionInfo(connection.getURI(), info); + } + + private static final class ProxyInfo implements ConnectionInfo { + + final Map env = new HashMap(); + final Map properties = new HashMap(); + + @Override + public Map getEnvironmentMap() { + return env; + } + + @Override + public Map getProperties() { + return properties; + } + } +} \ No newline at end of file diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionsPool.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionsPool.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.common; + +import java.net.URI; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionsRegistry.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ConnectionsRegistry.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.access.EnvironmentMapAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.EnvironmentMap; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/common/Connector.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/Connector.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.common; + +import java.io.IOException; +import java.io.InterruptedIOException; +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.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.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 IOException { + return connectImpl(uri, authDataProvider); + } + + private static ConnectionImplementation connectImpl(final URI uri, final AuthDataProvider authDataProvider) throws IOException { + final AtomicReference resultRef = new AtomicReference(); + final AtomicReference exRef = new AtomicReference(); + final String msg = NbBundle.getMessage(Connector.class, "Connector.progress.connectTo", uri.getHost()); // 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(); + } + } + + IOException 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 (IOException ex) { + exRef.compareAndSet(null, ex); + } catch (InterruptedException ex) { + exRef.compareAndSet(null, new InterruptedIOException(uri.toString())); + break; + } + } + + exRef.compareAndSet(null, new NativeExecutionException("Unsupported connection scheme: " + uri)); // NOI18N + return null; + } + + @Override + public boolean cancel() { + exRef.compareAndSet(null, new InterruptedIOException(uri.toString())); + if (thread != null) { + thread.interrupt(); + } + return true; + } + } +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/common/NativeProcess.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/NativeProcess.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/NativeProcess.java @@ -0,0 +1,437 @@ +/* + * 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.cnd.execution.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import org.netbeans.api.extexecution.process.ProcessCharset; +import org.netbeans.api.extexecution.process.ProcessId; +import org.netbeans.api.extexecution.process.ProcessSignal; +import org.netbeans.modules.cnd.execution.api.process.ProcessState; +import org.netbeans.modules.cnd.execution.api.process.ProcessState.State; +import org.netbeans.modules.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.access.NativeProcessAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.cnd.execution.spi.NativeProcessImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; + +/** + * An object that represents an external process. + * + * @author akrasny + */ +public final class NativeProcess extends Process implements Lookup.Provider { + + 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 final Lookup fixedLookup; + private Lookup lookup; + 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()); + } + + public 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(); + fixedLookup = Lookups.fixed( + new NativeProcessId(), + new NativeProcessCharset(), + new NativeProcessState(), + new NativeProcessSignal()); + } + + public NativeProcess start() throws NativeExecutionException { + try { + setState(State.STARTING); + processImpl = creator.createAndStart(params); + + if (processImpl instanceof Lookup.Provider) { + Lookup.Provider provider = (Lookup.Provider) processImpl; + lookup = new ProxyLookup(fixedLookup, provider.getLookup()); + } else { + lookup = fixedLookup; + } + + 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 (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.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() { + killProcess(); + } + + /** + * 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 + } + + @Override + public Lookup getLookup() { + return lookup; + } + + 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 = null; // 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; + } + + private class NativeProcessId extends ProcessId { + + @Override + protected Integer getId() { + return NativeProcess.this.getPID(); + } + } + + private class NativeProcessState extends ProcessState { + + @Override + protected State getState() { + return NativeProcess.this.getState(); + } + } + + private class NativeProcessCharset extends ProcessCharset { + + @Override + protected Charset getInputCharset() { + return getProcessInputCharset(NativeProcess.this); + } + + @Override + protected Charset getOutputCharset() { + return getProcessOutputCharset(NativeProcess.this); + } + + @Override + protected Charset getErrorCharset() { + return getProcessOutputCharset(NativeProcess.this); + } + } + + private class NativeProcessSignal extends ProcessSignal { + + @Override + protected void signal(Signal signal) { + SignalUtils.signal(NativeProcess.this, SignalUtils.translate(signal)); + } + + @Override + protected void signalGroup(Signal signal) { + SignalUtils.signalGrp(NativeProcess.this, SignalUtils.translate(signal)); + } + } + + 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/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ProcessStateChangeNotifier.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/ProcessStateChangeNotifier.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.common; + +import org.netbeans.modules.cnd.execution.api.process.ProcessState.State; + +/** + * + * @author akrasny + */ +public interface ProcessStateChangeNotifier { + + public void notifyProcessStateChange(final State newProcessState); +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/common/SignalUtils.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/SignalUtils.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/common/SignalUtils.java @@ -0,0 +1,285 @@ +/* + * 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.cnd.execution.common; + +import java.net.URI; +import java.util.EnumMap; +import java.util.Map; +import org.netbeans.api.extexecution.process.ProcessSignal; +import org.netbeans.modules.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.access.NativeProcessAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.cnd.execution.spi.support.SignalSupport; +import org.netbeans.modules.cnd.execution.spi.support.SignalSupportProvider; +import org.netbeans.modules.cnd.execution.util.HostInfo; +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); + } + + private static Map translation; + + public synchronized static Signal translate(ProcessSignal.Signal signal) { + if (translation == null) { + translation = new EnumMap(ProcessSignal.Signal.class); + for (Signal sig : Signal.values()) { + ProcessSignal.Signal api = sig.getApiSignal(); + if (api != null) { + translation.put(api, sig); + } + } + } + return translation.get(signal); + } + + /** + * Available signals + */ + public enum Signal { + + NULL(0, ProcessSignal.Signal.NULL), + SIGHUP(1, ProcessSignal.Signal.SIGHUP), + SIGINT(2, ProcessSignal.Signal.SIGINT), + SIGQUIT(3, ProcessSignal.Signal.SIGQUIT), + SIGILL(4, ProcessSignal.Signal.SIGILL), + SIGTRAP(5, ProcessSignal.Signal.SIGTRAP), + SIGABRT(6, ProcessSignal.Signal.SIGABRT), + SIGEMT(7, ProcessSignal.Signal.SIGEMT), + SIGFPE(8, ProcessSignal.Signal.SIGFPE), + SIGKILL(9, ProcessSignal.Signal.SIGKILL), + SIGBUS(10, ProcessSignal.Signal.SIGBUS), + SIGSEGV(11, ProcessSignal.Signal.SIGSEGV), + SIGSYS(12, ProcessSignal.Signal.SIGSYS), + SIGPIPE(13, ProcessSignal.Signal.SIGPIPE), + SIGALRM(14, ProcessSignal.Signal.SIGALRM), + SIGTERM(15, ProcessSignal.Signal.SIGTERM), + SIGUSR1(16, ProcessSignal.Signal.SIGUSR1), + SIGUSR2(17, ProcessSignal.Signal.SIGUSR2), + SIGCHLD(18, ProcessSignal.Signal.SIGCHLD), + SIGPWR(19, ProcessSignal.Signal.SIGPWR), + SIGWINCH(20, ProcessSignal.Signal.SIGWINCH), + SIGURG(21, ProcessSignal.Signal.SIGURG), + SIGPOLL(22, ProcessSignal.Signal.SIGPOLL), + SIGSTOP(23, ProcessSignal.Signal.SIGSTOP), + SIGTSTP(24, ProcessSignal.Signal.SIGTSTP), + SIGCONT(25, ProcessSignal.Signal.SIGCONT), + SIGTTIN(26, ProcessSignal.Signal.SIGTTIN), + SIGTTOU(27, ProcessSignal.Signal.SIGTTOU), + SIGVTALRM(28, ProcessSignal.Signal.SIGVTALRM), + SIGPROF(29, ProcessSignal.Signal.SIGPROF), + SIGXCPU(30, ProcessSignal.Signal.SIGXCPU), + SIGWAITING(32, ProcessSignal.Signal.SIGWAITING), + SIGLWP(33, ProcessSignal.Signal.SIGLWP), + SIGFREEZE(34, ProcessSignal.Signal.SIGFREEZE), + SIGTHAW(35, ProcessSignal.Signal.SIGTHAW), + SIGCANCEL(36, ProcessSignal.Signal.SIGCANCEL), + SIGLOST(37, ProcessSignal.Signal.SIGLOST), + SIGXRES(38, ProcessSignal.Signal.SIGXRES), + SIGJVM1(39, ProcessSignal.Signal.SIGJVM1); + private final int id; + + private final ProcessSignal.Signal apiSignal; + + private Signal(int id, ProcessSignal.Signal apiSignal) { + this.id = id; + this.apiSignal = apiSignal; + } + + /** + * 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 + } + + ProcessSignal.Signal getApiSignal() { + return apiSignal; + } + } +} \ No newline at end of file diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/resources/Bundle.properties b/cnd.execution/src/org/netbeans/modules/cnd/execution/resources/Bundle.properties new file mode 100755 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/resources/Bundle.properties @@ -0,0 +1,2 @@ +OpenIDE-Module-Name=Native Execution API + diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/AuthDataProvider.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/AuthDataProvider.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/AuthDataProvider.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.cnd.execution.spi; + +import java.net.URI; +import org.netbeans.modules.cnd.execution.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 + /** + * Common identifier used for requesting a User Interaction facility. + */ + public static final String USER_INTERACTION = "userinteraction"; // 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/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/ConnectionImplementation.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/ConnectionImplementation.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/ConnectionImplementation.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.cnd.execution.spi; + +import java.net.URI; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfoProvider; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/ConnectorImplementation.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/ConnectorImplementation.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/ConnectorImplementation.java @@ -0,0 +1,93 @@ +/* + * 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.cnd.execution.spi; + +import java.io.IOException; +import java.net.URI; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.cnd.execution.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, IOException; +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessCreator.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessCreator.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessCreator.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.cnd.execution.spi; + +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.openide.util.Lookup; + +/** + * An SPI interface for providing process creator implementation. + * + * @author akrasny + */ +public interface NativeProcessCreator extends Lookup.Provider { + + /** + * 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/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessImplementation.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessImplementation.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/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.cnd.execution.spi; + +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/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessParams.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessParams.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/NativeProcessParams.java @@ -0,0 +1,197 @@ +/* + * 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.cnd.execution.spi; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.netbeans.modules.cnd.execution.access.NativeProcessParamsFactory; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.modules.cnd.execution.common.NativeProcess; +import org.openide.util.Lookup; + +/** + * Parameters constructed by a {@link NativeProcessBuilder} to be used for + * {@link NativeProcess} creation. + * + *

This is an immutable object.

+ * + * @author akrasny + */ +public final class NativeProcessParams implements Lookup.Provider { + + private final Connection connection; + private final Map connectionInfo; + 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 final Lookup lookup; + + private NativeProcessParams(Connection connection, Map connectionInfo, + Map environmentMap, + List command, String shell, String shellScript, + boolean redirectError, String wdir, Lookup lookup) { + this.connection = connection; + this.connectionInfo = connectionInfo; + this.environment = environmentMap; + this.command = command; + this.shell = shell; + this.shellScript = shellScript; + this.redirectError = redirectError; + this.wdir = wdir; + this.lookup = lookup; + } + + /** + * 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); + } + + /** + * 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; + } + + @Override + public Lookup getLookup() { + return lookup; + } + + + // + static { + NativeProcessParamsFactory.setDefault(new NativeProcessParamsFactoryImpl()); + } + + private static class NativeProcessParamsFactoryImpl extends NativeProcessParamsFactory { + + @Override + public NativeProcessParams newNativeProcessParams(Connection connection, + Map connectionInfo, Map env, + List command, String shell, String shellScript, + boolean redirectError, String wdir, Lookup lookup) { + NativeProcessParams params = new NativeProcessParams(connection, connectionInfo, env, + command, shell, shellScript, redirectError, wdir, lookup); + return params; + } + } + // +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/URIIdentifier.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/URIIdentifier.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.spi; + +import java.net.URI; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/UserInteraction.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/UserInteraction.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/UserInteraction.java @@ -0,0 +1,55 @@ +/* + * 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.cnd.execution.spi; + +import java.net.URI; + +/** + * + * @author Andrew + */ +public interface UserInteraction { + + public boolean promptYesNo(URI uri, String message); + + public void notify(URI uri, String message); +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/ConnectionInfo.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/ConnectionInfo.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.spi.support; + +import java.util.Map; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.EnvironmentMap; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.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 Map getEnvironmentMap(); + + /** + * Returns a map of attributes to be associated with the connection. + * + * @return the map of connection attributes. + */ + public Map getProperties(); +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/ConnectionInfoProvider.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/ConnectionInfoProvider.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.spi.support; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/SignalSupport.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/SignalSupport.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.spi.support; + +import org.netbeans.modules.cnd.execution.common.NativeProcess; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.common.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/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/SignalSupportProvider.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/spi/support/SignalSupportProvider.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.spi.support; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/util/Computable.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/Computable.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.util; + +import org.netbeans.modules.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/util/HostInfo.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/HostInfo.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/HostInfo.java @@ -0,0 +1,413 @@ +/* + * 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.cnd.execution.util; + +import java.net.URI; +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import org.netbeans.modules.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.access.EnvironmentMapAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.EnvironmentMap; +import org.netbeans.modules.cnd.execution.common.ConnectionsInfo; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfoProvider; +import org.openide.util.Exceptions; + +/** + * 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 { + + public static final String UNKNOWN = "UNKNOWN"; // NOI18N + private final URI connectionURI; + + private HostInfo(final URI connectionURI) { + this.connectionURI = connectionURI; + } + + /** + * 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) { + return new HostInfo(connection.getURI()); + } + + public static EnvironmentMap getEnvironmentMap(Connection connection) { + ConnectionInfo cinfo = ConnectionsInfo.getConnectionInfo(connection.getURI()); + if (cinfo == null) { + return null; + } + + HostInfo hinfo = getFor(connection); + EnvironmentMap base; + EnvironmentMapAccessor access = EnvironmentMapAccessor.getDefault(); + switch (hinfo.getOS().family) { + case MACOSX: + base = access.createCasePreserving(':'); + break; + case WINDOWS: + base = access.createCasePreserving(';'); + break; + default: + base = access.createCaseSensitive(':'); + break; + } + base.putAll(cinfo.getEnvironmentMap().entrySet()); + return base; + } + + public String expandMacros(final String string) { + try { + return MacroExpander.expandMacros(string, getMacros()); + } catch (ParseException ex) { + Exceptions.printStackTrace(ex); + } + return string; + } + + /** + * Returns an OS object + * + * @return the OS object + */ + public OS getOS() { + return new OS( + get("OSFAMILY", OSFamily.UNKNOWN), // NOI18N + get("OSNAME", UNKNOWN), // NOI18N + get("OSVERSION", UNKNOWN), // NOI18N + getBitness("BITNESS", Bitness._32)); // NOI18N + } + + /** + * Returns a number of CPUs on the host. + * + * @return the number of CPUs on the host. + */ + public int getCpuNum() { + return get("CPUNUM", 1); // NOI18N + } + + /** + * Returns CpuFamily information. + * + * @return CpuFamily information. + */ + public CpuFamily getCpuFamily() { + return get("CPUFAMILY", CpuFamily.UNKNOWN); // NOI18N + } + + /** + * 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(getOS().family); + } + + /** + * Tests weather this is a Unix-like system. + * + * @return true if OSFamily is one of LINUX, MACOSX or SOLARIS + */ + public boolean isUnix() { + return getOS().family.isUnix(); + } + + /** + * Returns the login shell (as specified in /etc/passwd) of the connected + * user. + * + * @return the login shell + */ + public String getShell() { + return get("SHELL", "/bin/sh"); // NOI18N + } + + public String getTempDir() { + return get("TMPDIRBASE", "/var/tmp"); // 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; + + public boolean isUnix() { + switch (this) { + case LINUX: + case MACOSX: + case SOLARIS: + return true; + case WINDOWS: + return false; + case UNKNOWN: + return false; + default: + throw new IllegalStateException("Unexpected OSFamily: " + this); //NOI18N + } + } + + /** + * Returns CamelCase name of the family. + * + * Like: SunOS; Linux; Windows; MacOSX. + * + * @return CamelCase name + */ + public String cname() { + switch (this) { + case LINUX: + return "Linux"; // NOI18N + case MACOSX: + return "MacOSX"; // NOI18N + case SOLARIS: + return "SunOS"; // NOI18N + case WINDOWS: + return "Windows"; // NOI18N + case UNKNOWN: + return "UNKNOWN"; // NOI18N + default: + throw new IllegalStateException("Unexpected OSFamily: " + this); //NOI18N + } + } + } + + /** + * 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; + } + } + + // + private Map getMacros() { + Map map = new HashMap(); + String soext; + final OS os = getOS(); + switch (os.family) { + case WINDOWS: + soext = "dll"; // NOI18N + break; + case MACOSX: + soext = "dylib"; // NOI18N + break; + case SOLARIS: + case LINUX: + case UNKNOWN: + soext = "so"; // NOI18N + break; + default: + throw new IllegalStateException("Unexpected OSFamily: " + os.family); //NOI18N + } + + map.put("soext", soext); // NOI18N + map.put("osname", os.family.cname()); // NOI18N + map.put("isa", os.getBitness().toString()); // NOI18N + map.put("_isa", os.getBitness() == HostInfo.Bitness._64 ? "_64" : ""); // NOI18N + map.put("platform", getCpuFamily().name().toLowerCase()); // NOI18N + + return map; + } + + private String get(String key) { + ConnectionInfo info = ConnectionsInfo.getConnectionInfo(connectionURI); + if (info != null) { + return info.getProperties().get(key); + } + return null; + } + + @SuppressWarnings("unchecked") + private > T get(String name, T defaultValue) { + String val = get(name); + if (val != null) { + try { + T result = (T) Enum.valueOf(defaultValue.getClass(), val.toUpperCase()); + return result; + } catch (Throwable ex) { + ExecutionLogger.getInstance().log(Level.WARNING, + "HostInfo: Invalid value for {0} - {1}", // NOI18N + new Object[]{name, val}); + } + } + return defaultValue; + } + + private Bitness getBitness(String name, Bitness defaultValue) { + String val = get(name); + if (val != null) { + if ("64".equals(val)) { // NOI18N + return Bitness._64; + } + if ("32".equals(val)) { // NOI18N + return Bitness._32; + } + ExecutionLogger.getInstance().log(Level.WARNING, + "HostInfo: Invalid value for Bitness - {0}", // NOI18N + val); + } + return defaultValue; + } + + private String get(String name, String defaultValue) { + String val = get(name); + return val == null ? defaultValue : val; + } + + private int get(String name, int defaultValue) { + String val = get(name); + if (val != null) { + try { + return Integer.parseInt(val); + } catch (NumberFormatException ex) { + ExecutionLogger.getInstance().log(Level.WARNING, + "HostInfo: Invalid value for {0} - {1}", // NOI18N + new Object[]{name, val}); + } + } + return defaultValue; + } + // +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/util/MacroExpander.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/MacroExpander.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/util/ProcessUtils.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/ProcessUtils.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/ProcessUtils.java @@ -0,0 +1,124 @@ +/* + * 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.cnd.execution.util; + +import org.netbeans.modules.cnd.execution.common.SignalUtils; +import java.util.concurrent.TimeUnit; +import org.netbeans.modules.cnd.execution.common.NativeProcess; +import org.netbeans.modules.cnd.execution.common.SignalUtils.Signal; +import org.openide.util.RequestProcessor; + +/** + * 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() { + } + + /** + * 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 SIGTERM 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 Process 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) { + } + + if (!(process instanceof NativeProcess)) { + return; + } + + final NativeProcess nativeProcess = (NativeProcess) process; + try { + SignalUtils.signalGrp(nativeProcess, Signal.SIGTERM); + RP.schedule(new Runnable() { + + @Override + public void run() { + try { + process.exitValue(); + // No exception means successful termination + return; + } catch (IllegalThreadStateException ex) { + } + try { + SignalUtils.signalGrp(nativeProcess, Signal.SIGKILL); + } catch (UnsupportedOperationException ex) { + // ignore + } + } + }, 5, TimeUnit.SECONDS); + } catch (UnsupportedOperationException ex) { + // ignore + } + } +} diff --git a/cnd.execution/src/org/netbeans/modules/cnd/execution/util/TasksCachedProcessor.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/TasksCachedProcessor.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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.cnd.execution.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/cnd.execution/src/org/netbeans/modules/cnd/execution/util/URIMatcher.java b/cnd.execution/src/org/netbeans/modules/cnd/execution/util/URIMatcher.java new file mode 100644 --- /dev/null +++ b/cnd.execution/src/org/netbeans/modules/cnd/execution/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.cnd.execution.util; + +import java.net.URI; +import java.util.concurrent.atomic.AtomicReference; +import org.netbeans.modules.cnd.execution.common.ConnectionServiceLookup; +import org.netbeans.modules.cnd.execution.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/cnd.execution/test/unit/data/goldenfiles/org/netbeans/modules/nativeexecution/util/MacroMapTest/testPut.pass b/cnd.execution/test/unit/data/goldenfiles/org/netbeans/modules/nativeexecution/util/MacroMapTest/testPut.pass new file mode 100644 --- /dev/null +++ b/cnd.execution/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/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/ConnectionManagerTest.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/ConnectionManagerTest.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/ConnectionManagerTest.java @@ -0,0 +1,249 @@ +/* + * 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.cnd.execution.api; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +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/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/ConnectionTest.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/ConnectionTest.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/ConnectionTest.java @@ -0,0 +1,177 @@ +/* + * 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.cnd.execution.api; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.cnd.execution.api.ConnectionStateChangeListener; +import org.netbeans.modules.cnd.execution.api.ConnectionStateChangeEvent; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +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.cnd.execution.util.HostInfo; +import org.netbeans.modules.cnd.execution.util.HostInfo.CpuFamily; +import org.netbeans.modules.cnd.execution.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/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/TestConnectionIdentifier.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/TestConnectionIdentifier.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/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.cnd.execution.api; + +import java.net.URI; +import org.netbeans.modules.cnd.execution.spi.URIIdentifier; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.cnd.execution.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/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/TestConnector.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/TestConnector.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/api/TestConnector.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.cnd.execution.api; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.ConnectorImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.cnd.execution.spi.NativeProcessImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.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.cnd.execution.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 Map getEnvironmentMap() { + HashMap env = new HashMap(); + 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; + + @Override + public NativeProcessImplementation createAndStart(NativeProcessParams params) throws NativeExecutionException { + this.params = params; + throw new NativeExecutionException("Not supported"); + } + + @Override + public Lookup getLookup() { + return Lookup.EMPTY; + } + } +} \ No newline at end of file diff --git a/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/common/ConnectionsRegistryTest.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/common/ConnectionsRegistryTest.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/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.cnd.execution.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/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/EnvironmentMapTest.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/EnvironmentMapTest.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/EnvironmentMapTest.java @@ -0,0 +1,111 @@ +/* + * 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.cnd.execution.util; + +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.cnd.execution.access.EnvironmentMapAccessor; +import org.netbeans.modules.cnd.execution.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() { + EnvironmentMap map = EnvironmentMapAccessor.getDefault().createCasePreserving(';'); + map.put("Path", ""); + map.put("PATH", "${PATH};${UNKNOWN}"); + map.appendPath("path", ""); + + map.dump(System.out); + assertEquals(1, map.size()); + assertEquals(";${UNKNOWN};", map.get("path")); + + String name = map.entrySet().iterator().next().getKey(); + + // First time case was exactly Path - it should be preserved + assertEquals("Path", name); + + + map = EnvironmentMapAccessor.getDefault().createCaseSensitive(':'); + // map.setDictionary(special); + map.put("Path", ""); + map.put("PATH", "${PATH}:${UNKNOWN}"); + map.appendPath("path", ""); + map.prependPath("path", ""); + + map.dump(System.out); + assertEquals(3, map.entrySet().size()); + assertEquals(":", map.get("path")); + assertEquals("${PATH}:${UNKNOWN}", map.get("PATH")); + assertEquals("", map.get("Path")); + } +} diff --git a/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/HostInfoTest.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/HostInfoTest.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/HostInfoTest.java @@ -0,0 +1,117 @@ +/* + * 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.cnd.execution.util; + +import static org.junit.Assert.*; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Collections; +import java.util.concurrent.ExecutionException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.api.extexecution.ExecutionService; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.openide.util.Exceptions; + +/** + * + * @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()); + + File f = new File("/usr/bin/uname"); + if (!f.canExecute()) { + f = new File("/bin/uname"); + if (!f.canExecute()) { + fail("uname not found"); + } + } + try { + org.netbeans.api.extexecution.ProcessBuilder pb = local.getProcessBuilder(); + pb.setExecutable(f.getAbsolutePath()); + pb.setArguments(Collections.singletonList("-a")); + StringWriter output = new StringWriter(); + ExecutionService.newService(pb, output, null).run().get(); + System.out.println(output.toString()); + } catch (ExecutionException ex) { + Exceptions.printStackTrace(ex); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + + } +} diff --git a/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/MacroMapTest.java b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/MacroMapTest.java new file mode 100644 --- /dev/null +++ b/cnd.execution/test/unit/src/org/netbeans/modules/cnd/execution/util/MacroMapTest.java @@ -0,0 +1,198 @@ +/* + * 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.cnd.execution.util; + +import org.netbeans.modules.cnd.execution.api.EnvironmentMap; +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.netbeans.modules.cnd.execution.access.EnvironmentMapAccessor; +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 EnvironmentMap. + */ +// @Test + public void _testPut() { + EnvironmentMap map; + +// map = EnvironmentMapAccessor.getDefault().createCaseSensitive(':'); +// map.put("A", "1"); +// map.put("C", "2"); +// map.put("B", "3"); +// map.dump(getRef()); +// +// map = EnvironmentMapAccessor.getDefault().createCaseSensitive(':'); +// map.put("A", "1"); +// map.put("c", "2"); +// map.put("b", "3"); +// map.put("C", "4"); +// map.put("A", "5"); +// map.dump(getRef()); +// +// map = EnvironmentMapAccessor.getDefault().createCasePreserving(':'); +// map.put("A", "1"); +// map.put("C", "2"); +// map.put("B", "3"); +// map.dump(getRef()); +// +// map = EnvironmentMapAccessor.getDefault().createCasePreserving(':'); +// map.put("A", "1"); +// map.put("c", "2"); +// map.put("b", "3"); +// map.put("C", "4"); +// map.put("A", "5"); +// map.dump(getRef()); +// +// map = EnvironmentMapAccessor.getDefault().createCasePreserving(':'); +// map.put("A", "1"); +// map.put("C", "2"); +// map.put("B", "3"); +// map.dump(getRef()); +// +// map = EnvironmentMapAccessor.getDefault().createCasePreserving(':'); +// 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/cnd.makeproject/nbproject/project.xml b/cnd.makeproject/nbproject/project.xml --- a/cnd.makeproject/nbproject/project.xml +++ b/cnd.makeproject/nbproject/project.xml @@ -67,6 +67,14 @@ 1.9.2
+ + org.netbeans.modules.cnd.execution + + + + 1.0 + + org.netbeans.modules.cnd.source @@ -133,6 +141,14 @@ + org.netbeans.modules.execmerge + + + + 1.0 + + + org.netbeans.modules.extexecution diff --git a/cnd.makeproject/src/org/netbeans/modules/cnd/makeproject/api/ProjectActionSupport.java b/cnd.makeproject/src/org/netbeans/modules/cnd/makeproject/api/ProjectActionSupport.java --- a/cnd.makeproject/src/org/netbeans/modules/cnd/makeproject/api/ProjectActionSupport.java +++ b/cnd.makeproject/src/org/netbeans/modules/cnd/makeproject/api/ProjectActionSupport.java @@ -61,6 +61,7 @@ import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.Action; +import org.netbeans.api.extexecution.ProcessBuilder; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.api.project.Project; @@ -73,6 +74,7 @@ import org.netbeans.modules.cnd.api.remote.PathMap; import org.netbeans.modules.cnd.api.remote.RemoteFileUtil; import org.netbeans.modules.cnd.api.remote.RemoteSyncSupport; +import org.netbeans.modules.cnd.execution.api.Connection; import org.netbeans.modules.cnd.makeproject.MakeOptions; import org.netbeans.modules.cnd.makeproject.api.BuildActionsProvider.BuildAction; import org.netbeans.modules.cnd.makeproject.api.BuildActionsProvider.OutputStreamHandler; @@ -92,6 +94,7 @@ import org.netbeans.modules.cnd.utils.CndUtils; import org.netbeans.modules.cnd.utils.cache.CndFileUtils; import org.netbeans.modules.dlight.api.terminal.TerminalSupport; +import org.netbeans.modules.execmerge.ConnectionUtils; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.netbeans.modules.nativeexecution.api.ExecutionListener; import org.netbeans.modules.nativeexecution.api.execution.IOTabsController; @@ -881,7 +884,9 @@ } } } - TerminalSupport.openTerminal(getString("TargetExecutor.TermAction.tabTitle", projectName, env.getDisplayName()), env, dir); // NOI18N + + TerminalSupport.openTerminal(getString("TargetExecutor.TermAction.tabTitle", projectName, env.getDisplayName()), + ConnectionUtils.ExecutionEnvironment2Connection(env), dir); // NOI18N break; } } diff --git a/cnd.remote.projectui/nbproject/project.xml b/cnd.remote.projectui/nbproject/project.xml --- a/cnd.remote.projectui/nbproject/project.xml +++ b/cnd.remote.projectui/nbproject/project.xml @@ -67,6 +67,14 @@ + org.netbeans.modules.cnd.execution + + + + 1.0 + + + org.netbeans.modules.cnd.makeproject diff --git a/cnd.remote/nbproject/project.xml b/cnd.remote/nbproject/project.xml --- a/cnd.remote/nbproject/project.xml +++ b/cnd.remote/nbproject/project.xml @@ -69,6 +69,14 @@ + org.netbeans.modules.cnd.execution + + + + 1.0 + + + org.netbeans.modules.cnd.makeproject @@ -126,6 +134,14 @@ + org.netbeans.modules.execmerge + + + + 1.0 + + + org.netbeans.modules.extexecution diff --git a/cnd.remote/src/org/netbeans/modules/remote/ui/OpenTerminalAction.java b/cnd.remote/src/org/netbeans/modules/remote/ui/OpenTerminalAction.java --- a/cnd.remote/src/org/netbeans/modules/remote/ui/OpenTerminalAction.java +++ b/cnd.remote/src/org/netbeans/modules/remote/ui/OpenTerminalAction.java @@ -39,7 +39,6 @@ * * Portions Copyrighted 2011 Sun Microsystems, Inc. */ - package org.netbeans.modules.remote.ui; import java.io.IOException; @@ -48,6 +47,7 @@ import javax.swing.SwingUtilities; import org.netbeans.modules.cnd.remote.mapper.RemotePathMap; import org.netbeans.modules.dlight.api.terminal.TerminalSupport; +import org.netbeans.modules.execmerge.ConnectionUtils; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.netbeans.modules.nativeexecution.api.HostInfo; import org.netbeans.modules.nativeexecution.api.util.ConnectionManager; @@ -70,9 +70,10 @@ * @author Vladimir Voskresensky */ @ActionID(id = "org.netbeans.modules.remote.ui.OpenTerminalAction", category = "NativeRemote") -@ActionRegistration(displayName = "OpenTerminalMenuItem") +@ActionRegistration(displayName = "OpenTerminalMenuItem", lazy = false) @ActionReference(path = "Remote/Host/Actions", name = "OpenTerminalAction", position = 700) public class OpenTerminalAction extends SingleHostAction { + private JMenu remotePopupMenu; private JMenuItem localPopupMenu; @@ -88,7 +89,7 @@ Node[] activatedNodes = getActivatedNodes(); if (activatedNodes != null && activatedNodes.length == 1 && !isRemote(activatedNodes[0])) { SystemAction.get(AddHome.class).performAction(env, node); - } + } } @Override @@ -175,7 +176,7 @@ @Override public void run() { - TerminalSupport.openTerminal(env.getDisplayName(), env, path); + TerminalSupport.openTerminal(env.getDisplayName(), ConnectionUtils.ExecutionEnvironment2Connection(env), path); } }; @@ -235,7 +236,6 @@ } return null; } - private static final class AddMirror extends AddPlace { @@ -249,5 +249,4 @@ return remoteSyncRoot; } } - } diff --git a/cnd.search/nbproject/project.xml b/cnd.search/nbproject/project.xml --- a/cnd.search/nbproject/project.xml +++ b/cnd.search/nbproject/project.xml @@ -14,11 +14,19 @@ + org.netbeans.modules.cnd.execution + + + + 1.0 + + + org.netbeans.modules.dlight.nativeexecution - 1.14 + 1.21 @@ -35,7 +43,7 @@ 2 - 1.30 + 1.37 diff --git a/cnd.search/src/org/netbeans/modules/cnd/search/SearchResult.java b/cnd.search/src/org/netbeans/modules/cnd/search/SearchResult.java --- a/cnd.search/src/org/netbeans/modules/cnd/search/SearchResult.java +++ b/cnd.search/src/org/netbeans/modules/cnd/search/SearchResult.java @@ -41,7 +41,7 @@ */ package org.netbeans.modules.cnd.search; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; +import org.netbeans.modules.cnd.execution.api.Connection; import org.netbeans.modules.remote.spi.FileSystemProvider; import org.openide.filesystems.FileObject; @@ -51,11 +51,11 @@ */ public final class SearchResult { - public final ExecutionEnvironment env; + public final Connection connection; public final MatchingFileData data; - public SearchResult(ExecutionEnvironment env, MatchingFileData matchingFile) { - this.env = env; + public SearchResult(Connection connection, MatchingFileData matchingFile) { + this.connection = connection; this.data = matchingFile; } @@ -64,6 +64,6 @@ return null; } - return FileSystemProvider.getFileObject(env, data.getPath()); + return FileSystemProvider.getFileObject(connection, data.getPath()); } } diff --git a/cnd.search/src/org/netbeans/modules/cnd/search/impl/SearchBrowseHostScope.java b/cnd.search/src/org/netbeans/modules/cnd/search/impl/SearchBrowseHostScope.java --- a/cnd.search/src/org/netbeans/modules/cnd/search/impl/SearchBrowseHostScope.java +++ b/cnd.search/src/org/netbeans/modules/cnd/search/impl/SearchBrowseHostScope.java @@ -41,6 +41,7 @@ */ package org.netbeans.modules.cnd.search.impl; +import java.net.URI; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -50,8 +51,7 @@ import org.netbeans.api.search.provider.SearchInfoUtils; import org.netbeans.api.search.provider.SearchListener; import org.netbeans.modules.cnd.search.ui.DirectoryChooser; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; import org.netbeans.modules.remote.spi.FileSystemProvider; import org.netbeans.spi.search.SearchInfoDefinition; import org.netbeans.spi.search.SearchScopeDefinition; @@ -64,144 +64,144 @@ * @author akrasny */ public final class SearchBrowseHostScope { - - private static FileObject root; - private final ExecutionEnvironment env; - private SearchScopeDefinition browseScope = new BrowseHostScopeDefinition(); - private SearchScopeDefinition lastScope = new LastSearchBrowseHostScope(); - - public SearchBrowseHostScope(ExecutionEnvironment env) { - this.env = env; - } - - final class BrowseHostScopeDefinition extends SearchScopeDefinition { - - private final SearchInfo searchInfo; - - public BrowseHostScopeDefinition() { - searchInfo = SearchInfoUtils.createForDefinition(new BrowseHostInfoDefinition()); - } - - @Override - public String getTypeId() { - return SearchBrowseHostScope.class.getName(); - } - - @Override - public String getDisplayName() { - return NbBundle.getMessage(SearchBrowseHostScope.class, "LBL_BrowseHostScopeDefinitionName", env.getDisplayName()); // NOI18N - } - - @Override - public boolean isApplicable() { - return ConnectionManager.getInstance().isConnectedTo(env); - } - - @Override - public SearchInfo getSearchInfo() { - return searchInfo; - } - - @Override - public int getPriority() { - return 701; - } - - @Override - public void clean() { - } - - @Override - public void selected() { - chooseRoots(); - notifyListeners(); - } - - private FileObject[] chooseRoots() { - FileObject dir = DirectoryChooser.chooseDirectory(WindowManager.getDefault().getMainWindow(), env, null); - if (dir != null) { - root = dir; - return new FileObject[]{root}; - } - return new FileObject[0]; - } - - private class BrowseHostInfoDefinition extends SearchInfoDefinition { - - private SearchInfo delegate; - - @Override - public boolean canSearch() { - return true; - } - - @Override - public Iterator filesToSearch(SearchScopeOptions options, SearchListener listener, AtomicBoolean terminated) { - return getDelegate().getFilesToSearch(options, listener, terminated).iterator(); - } - - @Override - public List getSearchRoots() { - return getDelegate().getSearchRoots(); - } - - private synchronized SearchInfo getDelegate() { - if (delegate == null) { - delegate = createDelegate(); - } - return delegate; - } - - private SearchInfo createDelegate() { - FileObject[] fileObjects = chooseRoots(); - return SearchInfoUtils.createSearchInfoForRoots(fileObjects); - } - } - } - - private class LastSearchBrowseHostScope extends SearchScopeDefinition { - - @Override - public String getTypeId() { - return SearchBrowseHostScope.class.getName(); - } - - @Override - public String getDisplayName() { - if (root != null) { - return NbBundle.getMessage(SearchBrowseHostScope.class, "LBL_BrowseHostScopeBrowseName", // NOI18N - root.getNameExt(), - FileSystemProvider.getExecutionEnvironment(root).getDisplayName()); - } else { - return NbBundle.getMessage(SearchBrowseHostScope.class, "LBL_NoSelection"); // NOI18N - } - } - - @Override - public boolean isApplicable() { - return root != null; - } - - @Override - public SearchInfo getSearchInfo() { - return SearchInfoUtils.createSearchInfoForRoots(new FileObject[]{root}); - } - - @Override - public int getPriority() { - return 700; - } - - @Override - public void clean() { - } - } - - public SearchScopeDefinition getBrowseScope() { - return browseScope; - } - - public SearchScopeDefinition getLastScope() { - return lastScope; - } +// +// private static FileObject root; +// private final URI uri; +// private SearchScopeDefinition browseScope = new BrowseHostScopeDefinition(); +// private SearchScopeDefinition lastScope = new LastSearchBrowseHostScope(); +// +// public SearchBrowseHostScope(URI uri) { +// this.uri = uri; +// } +// +// final class BrowseHostScopeDefinition extends SearchScopeDefinition { +// +// private final SearchInfo searchInfo; +// +// public BrowseHostScopeDefinition() { +// searchInfo = SearchInfoUtils.createForDefinition(new BrowseHostInfoDefinition()); +// } +// +// @Override +// public String getTypeId() { +// return SearchBrowseHostScope.class.getName(); +// } +// +// @Override +// public String getDisplayName() { +// return NbBundle.getMessage(SearchBrowseHostScope.class, "LBL_BrowseHostScopeDefinitionName", uri); // NOI18N +// } +// +// @Override +// public boolean isApplicable() { +// return ConnectionManager.getConnection(uri) != null; +// } +// +// @Override +// public SearchInfo getSearchInfo() { +// return searchInfo; +// } +// +// @Override +// public int getPriority() { +// return 701; +// } +// +// @Override +// public void clean() { +// } +// +// @Override +// public void selected() { +// chooseRoots(); +// notifyListeners(); +// } +// +// private FileObject[] chooseRoots() { +// FileObject dir = DirectoryChooser.chooseDirectory(WindowManager.getDefault().getMainWindow(), uri, null); +// if (dir != null) { +// root = dir; +// return new FileObject[]{root}; +// } +// return new FileObject[0]; +// } +// +// private class BrowseHostInfoDefinition extends SearchInfoDefinition { +// +// private SearchInfo delegate; +// +// @Override +// public boolean canSearch() { +// return true; +// } +// +// @Override +// public Iterator filesToSearch(SearchScopeOptions options, SearchListener listener, AtomicBoolean terminated) { +// return getDelegate().getFilesToSearch(options, listener, terminated).iterator(); +// } +// +// @Override +// public List getSearchRoots() { +// return getDelegate().getSearchRoots(); +// } +// +// private synchronized SearchInfo getDelegate() { +// if (delegate == null) { +// delegate = createDelegate(); +// } +// return delegate; +// } +// +// private SearchInfo createDelegate() { +// FileObject[] fileObjects = chooseRoots(); +// return SearchInfoUtils.createSearchInfoForRoots(fileObjects); +// } +// } +// } +// +// private class LastSearchBrowseHostScope extends SearchScopeDefinition { +// +// @Override +// public String getTypeId() { +// return SearchBrowseHostScope.class.getName(); +// } +// +// @Override +// public String getDisplayName() { +// if (root != null) { +// return NbBundle.getMessage(SearchBrowseHostScope.class, "LBL_BrowseHostScopeBrowseName", // NOI18N +// root.getNameExt(), +// FileSystemProvider.getExecutionEnvironment(root).getDisplayName()); +// } else { +// return NbBundle.getMessage(SearchBrowseHostScope.class, "LBL_NoSelection"); // NOI18N +// } +// } +// +// @Override +// public boolean isApplicable() { +// return root != null; +// } +// +// @Override +// public SearchInfo getSearchInfo() { +// return SearchInfoUtils.createSearchInfoForRoots(new FileObject[]{root}); +// } +// +// @Override +// public int getPriority() { +// return 700; +// } +// +// @Override +// public void clean() { +// } +// } +// +// public SearchScopeDefinition getBrowseScope() { +// return browseScope; +// } +// +// public SearchScopeDefinition getLastScope() { +// return lastScope; +// } } diff --git a/cnd.search/src/org/netbeans/modules/cnd/search/impl/spi/CNDSearchComposition.java b/cnd.search/src/org/netbeans/modules/cnd/search/impl/spi/CNDSearchComposition.java --- a/cnd.search/src/org/netbeans/modules/cnd/search/impl/spi/CNDSearchComposition.java +++ b/cnd.search/src/org/netbeans/modules/cnd/search/impl/spi/CNDSearchComposition.java @@ -41,9 +41,13 @@ */ package org.netbeans.modules.cnd.search.impl.spi; -import java.io.BufferedReader; import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; +import org.netbeans.api.extexecution.ExecutionService; +import org.netbeans.api.extexecution.input.LineProcessor; import org.netbeans.api.search.SearchRoot; import org.netbeans.api.search.provider.SearchListener; import org.netbeans.modules.cnd.search.MatchingFileData; @@ -54,12 +58,8 @@ import org.netbeans.modules.cnd.search.ui.SearchResultNode; import org.netbeans.modules.cnd.search.ui.SearchResultPropertySet; import org.netbeans.modules.cnd.search.util.OutlineSupport; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.NativeProcess; -import org.netbeans.modules.nativeexecution.api.NativeProcessBuilder; -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager; -import org.netbeans.modules.nativeexecution.api.util.HostInfoUtils; -import org.netbeans.modules.nativeexecution.api.util.ProcessUtils; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.util.HostInfo; import org.netbeans.modules.remote.spi.FileSystemProvider; import org.netbeans.spi.search.provider.DefaultSearchResultsDisplayer; import org.netbeans.spi.search.provider.SearchComposition; @@ -69,9 +69,7 @@ import org.netbeans.spi.search.provider.SearchResultsDisplayer.NodeDisplayer; import org.openide.explorer.view.OutlineView; import org.openide.nodes.Node; -import org.openide.util.Cancellable; -import org.openide.util.RequestProcessor; -import org.openide.util.RequestProcessor.Task; +import org.openide.util.Exceptions; /** * @@ -79,13 +77,12 @@ */ public final class CNDSearchComposition extends SearchComposition { - private static final RequestProcessor RP = new RequestProcessor(CNDSearchComposition.class.getName(), 2); private final AtomicBoolean terminated = new AtomicBoolean(false); private final SearchParams params; private DefaultSearchResultsDisplayer displayer; private final String title; private final Presenter presenter; - private Cancellable cancel; + private Future task; public CNDSearchComposition(String title, SearchProvider.Presenter presenter, SearchParams params) { this.title = title; @@ -109,9 +106,9 @@ @Override public void terminate() { if (terminated.compareAndSet(false, true)) { - if (cancel != null) { - cancel.cancel(); - cancel = null; + if (task != null) { + task.cancel(true); + task = null; } } } @@ -134,102 +131,75 @@ } private void searchInRoot(SearchRoot root, final SearchListener listener) { - if (isTerminated()) { - return; - } - - final ExecutionEnvironment env = FileSystemProvider.getExecutionEnvironment(root.getFileObject()); - - if (!ConnectionManager.getInstance().isConnectedTo(env)) { - return; - } - - if (env.isRemote() && HostInfoUtils.isHostInfoAvailable(env)) { + final Connection connection = FileSystemProvider.getConnection(root.getFileObject()); + if (!connection.isConnected()) { return; } final Searcher find; - try { - if (HostInfoUtils.getHostInfo(env).getOSFamily().isUnix()) { - find = new UnixFindBasedSearcher(root, params); - } else { - find = null; - } - } catch (Exception ex) { - return; + + if (HostInfo.getFor(connection).isUnix()) { + find = new UnixFindBasedSearcher(root, params); + } else { + find = null; } if (find == null) { return; } - NativeProcessBuilder npb = NativeProcessBuilder.newProcessBuilder(env); - npb.setExecutable(find.getCommand()).setArguments(find.getCommandArguments()); + try { + final org.netbeans.api.extexecution.ProcessBuilder npb = connection.getProcessBuilder(); + npb.setExecutable(find.getCommand()); + npb.setArguments(Arrays.asList(find.getCommandArguments())); + task = ExecutionService.newService(npb, new LineProcessor() { + @Override + public void processLine(String line) { + if (isTerminated()) { + task.cancel(true); + } + + MatchingFileData data = find.processOutputLine(line.toString().trim()); + + if (data != null) { + displayer.addMatchingObject(new SearchResult(connection, data)); + } + } + @Override + public void reset() { + } + + @Override + public void close() { + } + }, new LineProcessor() { + @Override + public void processLine(String line) { + if (isTerminated()) { + task.cancel(true); + } + + Throwable ex = new Throwable(line.toString()); + listener.generalError(ex); + } + @Override + public void reset() { + } + + @Override + public void close() { + } + }, null).run(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } try { - final NativeProcess process = npb.call(); - final Runnable readOutputTask = new Runnable() { - - @Override - public void run() { - try { - BufferedReader br = ProcessUtils.getReader(process.getInputStream(), env.isRemote()); - String line; - while (!isTerminated() && (line = br.readLine()) != null) { - MatchingFileData data = find.processOutputLine(line.trim()); - - if (data != null) { - displayer.addMatchingObject(new SearchResult(env, data)); - } - } - } catch (IOException ex) { - if (!isTerminated()) { - listener.generalError(ex); - } - } - } - }; - - final Runnable readErrorTask = new Runnable() { - - @Override - public void run() { - try { - BufferedReader br = ProcessUtils.getReader(process.getErrorStream(), env.isRemote()); - String line; - while (!isTerminated() && (line = br.readLine()) != null) { - Throwable ex = new Throwable(line); - listener.generalError(ex); - } - } catch (IOException ex) { - if (!isTerminated()) { - listener.generalError(ex); - } - } - } - }; - - final Task outTask = RP.post(readOutputTask); - final Task errTask = RP.post(readErrorTask); - - cancel = new Cancellable() { - - @Override - public boolean cancel() { - process.destroy(); - outTask.cancel(); - errTask.cancel(); - outTask.waitFinished(); - errTask.waitFinished(); - return true; - } - }; - - process.waitFor(); + task.get(); } catch (InterruptedException ex) { - // Exceptions.printStackTrace(ex); - } catch (IOException ex) { - listener.generalError(ex); + Exceptions.printStackTrace(ex); + } catch (ExecutionException ex) { + Exceptions.printStackTrace(ex); } } diff --git a/cnd.search/src/org/netbeans/modules/cnd/search/ui/CNDSearchPanel.java b/cnd.search/src/org/netbeans/modules/cnd/search/ui/CNDSearchPanel.java --- a/cnd.search/src/org/netbeans/modules/cnd/search/ui/CNDSearchPanel.java +++ b/cnd.search/src/org/netbeans/modules/cnd/search/ui/CNDSearchPanel.java @@ -55,9 +55,6 @@ import org.netbeans.api.search.ui.FileNameController; import org.netbeans.api.search.ui.ScopeController; import org.netbeans.api.search.ui.SearchPatternController; -import org.netbeans.modules.cnd.search.impl.SearchBrowseHostScope; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager; import org.netbeans.spi.search.SearchScopeDefinition; import org.openide.util.ChangeSupport; import org.openide.util.NbBundle; @@ -222,13 +219,14 @@ private SearchScopeDefinition[] getAdditionalSearchScopes() { List result = new ArrayList(); - List recentConnections = ConnectionManager.getInstance().getRecentConnections(); - - for (ExecutionEnvironment env : recentConnections) { - SearchBrowseHostScope scope = new SearchBrowseHostScope(env); - result.add(scope.getBrowseScope()); - result.add(scope.getLastScope()); - } +// ConnectionManager. +// List recentConnections = ConnectionManager.getInstance().getRecentConnections(); +// +// for (ExecutionEnvironment env : recentConnections) { +// SearchBrowseHostScope scope = new SearchBrowseHostScope(env); +// result.add(scope.getBrowseScope()); +// result.add(scope.getLastScope()); +// } return result.toArray(new SearchScopeDefinition[result.size()]); } diff --git a/cnd.search/src/org/netbeans/modules/cnd/search/ui/DirectoryChooser.java b/cnd.search/src/org/netbeans/modules/cnd/search/ui/DirectoryChooser.java --- a/cnd.search/src/org/netbeans/modules/cnd/search/ui/DirectoryChooser.java +++ b/cnd.search/src/org/netbeans/modules/cnd/search/ui/DirectoryChooser.java @@ -50,7 +50,7 @@ import java.io.File; import javax.swing.*; import javax.swing.filechooser.FileFilter; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; +import org.netbeans.modules.cnd.execution.api.Connection; import org.netbeans.modules.remote.api.ui.FileChooserBuilder; import org.netbeans.modules.remote.spi.FileSystemProvider; import org.openide.DialogDescriptor; @@ -71,12 +71,12 @@ private FileObject validatedFileObject = null; private Dialog dialog; - public static FileObject chooseDirectory(Frame window, ExecutionEnvironment env, String initPath) { - DirectoryChooser chooser = new DirectoryChooser(env, initPath); + public static FileObject chooseDirectory(Frame window, Connection connection, String initPath) { + DirectoryChooser chooser = new DirectoryChooser(connection, initPath); boolean cancelled = false; DialogDescriptor dd = new DialogDescriptor(chooser, - NbBundle.getMessage(DirectoryChooser.class, "DirectoryChooser.title", env.getDisplayName()), // NOI18N + NbBundle.getMessage(DirectoryChooser.class, "DirectoryChooser.title", connection.toString()), // NOI18N true, new Object[]{chooser.selectButton, DialogDescriptor.CANCEL_OPTION}, chooser.selectButton, DialogDescriptor.DEFAULT_ALIGN, null, null, true); @@ -96,11 +96,11 @@ /** * Creates new form SearchRootChooser */ - private DirectoryChooser(ExecutionEnvironment env, String path) { + private DirectoryChooser(Connection connection, String path) { initComponents(); selectButton = new JButton(); Mnemonics.setLocalizedText(selectButton, NbBundle.getMessage(DirectoryChooser.class, "DirectoryChooser.ok_button.text")); // NOI18N - FileChooserBuilder fcBuilder = new FileChooserBuilder(env); + FileChooserBuilder fcBuilder = new FileChooserBuilder(connection); chooser = fcBuilder.createFileChooser(); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); diff --git a/cnd.search/src/org/netbeans/modules/cnd/search/ui/SearchResultNode.java b/cnd.search/src/org/netbeans/modules/cnd/search/ui/SearchResultNode.java --- a/cnd.search/src/org/netbeans/modules/cnd/search/ui/SearchResultNode.java +++ b/cnd.search/src/org/netbeans/modules/cnd/search/ui/SearchResultNode.java @@ -97,8 +97,8 @@ public Image getIcon(int type) { Object dobRefProp = getValue(DOB_REF_PROP); if (dobRefProp instanceof WeakReference) { - WeakReference dobRef = (WeakReference)dobRefProp; - DataObject dob = dobRef.get(); + WeakReference dobRef = (WeakReference)dobRefProp; + DataObject dob = (DataObject) dobRef.get(); if (dob != null) { return dob.getNodeDelegate().getIcon(type); } diff --git a/cnd.toolchain/nbproject/project.xml b/cnd.toolchain/nbproject/project.xml --- a/cnd.toolchain/nbproject/project.xml +++ b/cnd.toolchain/nbproject/project.xml @@ -91,6 +91,14 @@ + org.netbeans.modules.cnd.execution + + + + 1.0 + + + org.netbeans.modules.options.api diff --git a/dlight.nativeexecution/external/binaries-list b/dlight.nativeexecution/external/binaries-list --- a/dlight.nativeexecution/external/binaries-list +++ b/dlight.nativeexecution/external/binaries-list @@ -1,2 +1,2 @@ -886018BFD299115709FB351D28D9C1F5A2C6F5FD exechlp-1.0.zip +7AA7A8C9DA31FCB920B0613EC8C49229801FB6C6 exechlp-1.1.zip diff --git a/dlight.nativeexecution/external/exechlp-1.0-license.txt b/dlight.nativeexecution/external/exechlp-1.0-license.txt deleted file mode 100644 --- a/dlight.nativeexecution/external/exechlp-1.0-license.txt +++ /dev/null @@ -1,19 +0,0 @@ -Name: exechlp -Version: 1.0 -Description: binaries that used by dlight.nativeexecution module -License: TBD -OSR: - -Origin: - -Files: exechlp-1.0.zip contains killall process_start pty pty_open sigqueue stat unbuffer.so for different platforms -Source: - -Comment: needed for dlight.nativeexecution module to provide system-dependent services - -process_start, pty, pty_open - used for dealing with pseude terminals -sigqueue, killall - sending signals to processes -stat - getting detailed file information -unbuffer.so - used when unbuffering is required - -Use of exechlp version 1.0 is governed by the terms of the license below: - -TBD - diff --git a/dlight.nativeexecution/external/exechlp-1.1-license.txt b/dlight.nativeexecution/external/exechlp-1.1-license.txt new file mode 100644 --- /dev/null +++ b/dlight.nativeexecution/external/exechlp-1.1-license.txt @@ -0,0 +1,19 @@ +Name: exechlp +Version: 1.1 +Description: binaries that used by dlight.nativeexecution module +License: TBD +OSR: - +Origin: - +Files: exechlp-1.1.zip contains killall process_start pty pty_open sigqueue stat unbuffer.so for different platforms +Source: - +Comment: needed for dlight.nativeexecution module to provide system-dependent services + +process_start, pty, pty_open - used for dealing with pseude terminals +sigqueue, killall - sending signals to processes +stat - getting detailed file information +unbuffer.so - used when unbuffering is required + +Use of exechlp version 1.1 is governed by the terms of the license below: + +TBD + diff --git a/dlight.nativeexecution/nbproject/project.properties b/dlight.nativeexecution/nbproject/project.properties --- a/dlight.nativeexecution/nbproject/project.properties +++ b/dlight.nativeexecution/nbproject/project.properties @@ -21,72 +21,72 @@ **/RegisterDerbyTest.class,\ **/TerminalConfigurationProviderTest.class -release.external/exechlp-1.0.zip!/Linux-x86/process_start = bin/nativeexecution/Linux-x86/process_start -release.external/exechlp-1.0.zip!/Linux-x86/pty = bin/nativeexecution/Linux-x86/pty -release.external/exechlp-1.0.zip!/Linux-x86/pty_open = bin/nativeexecution/Linux-x86/pty_open -release.external/exechlp-1.0.zip!/Linux-x86/sigqueue = bin/nativeexecution/Linux-x86/sigqueue -release.external/exechlp-1.0.zip!/Linux-x86/stat = bin/nativeexecution/Linux-x86/stat -release.external/exechlp-1.0.zip!/Linux-x86/unbuffer.so = bin/nativeexecution/Linux-x86/unbuffer.so -release.external/exechlp-1.0.zip!/Linux-x86/killall = bin/nativeexecution/Linux-x86/killall -release.external/exechlp-1.0.zip!/Linux-x86_64/process_start = bin/nativeexecution/Linux-x86_64/process_start -release.external/exechlp-1.0.zip!/Linux-x86_64/pty = bin/nativeexecution/Linux-x86_64/pty -release.external/exechlp-1.0.zip!/Linux-x86_64/pty_open = bin/nativeexecution/Linux-x86_64/pty_open -release.external/exechlp-1.0.zip!/Linux-x86_64/sigqueue = bin/nativeexecution/Linux-x86_64/sigqueue -release.external/exechlp-1.0.zip!/Linux-x86_64/stat = bin/nativeexecution/Linux-x86_64/stat -release.external/exechlp-1.0.zip!/Linux-x86_64/unbuffer.so = bin/nativeexecution/Linux-x86_64/unbuffer.so -release.external/exechlp-1.0.zip!/Linux-x86_64/killall = bin/nativeexecution/Linux-x86_64/killall -release.external/exechlp-1.0.zip!/Windows-x86/process_start = bin/nativeexecution/Windows-x86/process_start -release.external/exechlp-1.0.zip!/Windows-x86/pty = bin/nativeexecution/Windows-x86/pty -release.external/exechlp-1.0.zip!/Windows-x86/pty_open = bin/nativeexecution/Windows-x86/pty_open -release.external/exechlp-1.0.zip!/Windows-x86/sigqueue = bin/nativeexecution/Windows-x86/sigqueue -release.external/exechlp-1.0.zip!/Windows-x86/unbuffer.dll = bin/nativeexecution/Windows-x86/unbuffer.dll -release.external/exechlp-1.0.zip!/Windows-x86/killall = bin/nativeexecution/Windows-x86/killall -release.external/exechlp-1.0.zip!/Windows-x86_64/process_start = bin/nativeexecution/Windows-x86_64/process_start -release.external/exechlp-1.0.zip!/Windows-x86_64/pty = bin/nativeexecution/Windows-x86_64/pty -release.external/exechlp-1.0.zip!/Windows-x86_64/pty_open = bin/nativeexecution/Windows-x86_64/pty_open -release.external/exechlp-1.0.zip!/Windows-x86_64/sigqueue = bin/nativeexecution/Windows-x86_64/sigqueue -release.external/exechlp-1.0.zip!/Windows-x86_64/killall = bin/nativeexecution/Windows-x86_64/killall -release.external/exechlp-1.0.zip!/MacOSX-x86/process_start = bin/nativeexecution/MacOSX-x86/process_start -release.external/exechlp-1.0.zip!/MacOSX-x86/pty = bin/nativeexecution/MacOSX-x86/pty -release.external/exechlp-1.0.zip!/MacOSX-x86/pty_open = bin/nativeexecution/MacOSX-x86/pty_open -release.external/exechlp-1.0.zip!/MacOSX-x86/stat = bin/nativeexecution/MacOSX-x86/stat -release.external/exechlp-1.0.zip!/MacOSX-x86/unbuffer.dylib = bin/nativeexecution/MacOSX-x86/unbuffer.dylib -release.external/exechlp-1.0.zip!/MacOSX-x86/killall = bin/nativeexecution/MacOSX-x86/killall -release.external/exechlp-1.0.zip!/MacOSX-x86_64/process_start = bin/nativeexecution/MacOSX-x86_64/process_start -release.external/exechlp-1.0.zip!/MacOSX-x86_64/pty = bin/nativeexecution/MacOSX-x86_64/pty -release.external/exechlp-1.0.zip!/MacOSX-x86_64/pty_open = bin/nativeexecution/MacOSX-x86_64/pty_open -release.external/exechlp-1.0.zip!/MacOSX-x86_64/stat = bin/nativeexecution/MacOSX-x86_64/stat -release.external/exechlp-1.0.zip!/MacOSX-x86_64/unbuffer.dylib = bin/nativeexecution/MacOSX-x86_64/unbuffer.dylib -release.external/exechlp-1.0.zip!/MacOSX-x86_64/killall = bin/nativeexecution/MacOSX-x86_64/killall -release.external/exechlp-1.0.zip!/SunOS-sparc/privp = bin/nativeexecution/SunOS-sparc/privp -release.external/exechlp-1.0.zip!/SunOS-sparc/process_start = bin/nativeexecution/SunOS-sparc/process_start -release.external/exechlp-1.0.zip!/SunOS-sparc/pty = bin/nativeexecution/SunOS-sparc/pty -release.external/exechlp-1.0.zip!/SunOS-sparc/pty_open = bin/nativeexecution/SunOS-sparc/pty_open -release.external/exechlp-1.0.zip!/SunOS-sparc/sigqueue = bin/nativeexecution/SunOS-sparc/sigqueue -release.external/exechlp-1.0.zip!/SunOS-sparc/stat = bin/nativeexecution/SunOS-sparc/stat -release.external/exechlp-1.0.zip!/SunOS-sparc/unbuffer.so = bin/nativeexecution/SunOS-sparc/unbuffer.so -release.external/exechlp-1.0.zip!/SunOS-sparc/killall = bin/nativeexecution/SunOS-sparc/killall -release.external/exechlp-1.0.zip!/SunOS-sparc_64/privp = bin/nativeexecution/SunOS-sparc_64/privp -release.external/exechlp-1.0.zip!/SunOS-sparc_64/process_start = bin/nativeexecution/SunOS-sparc_64/process_start -release.external/exechlp-1.0.zip!/SunOS-sparc_64/pty = bin/nativeexecution/SunOS-sparc_64/pty -release.external/exechlp-1.0.zip!/SunOS-sparc_64/pty_open = bin/nativeexecution/SunOS-sparc_64/pty_open -release.external/exechlp-1.0.zip!/SunOS-sparc_64/sigqueue = bin/nativeexecution/SunOS-sparc_64/sigqueue -release.external/exechlp-1.0.zip!/SunOS-sparc_64/stat = bin/nativeexecution/SunOS-sparc_64/stat -release.external/exechlp-1.0.zip!/SunOS-sparc_64/unbuffer.so = bin/nativeexecution/SunOS-sparc_64/unbuffer.so -release.external/exechlp-1.0.zip!/SunOS-sparc_64/killall = bin/nativeexecution/SunOS-sparc_64/killall -release.external/exechlp-1.0.zip!/SunOS-x86/privp = bin/nativeexecution/SunOS-x86/privp -release.external/exechlp-1.0.zip!/SunOS-x86/process_start = bin/nativeexecution/SunOS-x86/process_start -release.external/exechlp-1.0.zip!/SunOS-x86/pty = bin/nativeexecution/SunOS-x86/pty -release.external/exechlp-1.0.zip!/SunOS-x86/pty_open = bin/nativeexecution/SunOS-x86/pty_open -release.external/exechlp-1.0.zip!/SunOS-x86/sigqueue = bin/nativeexecution/SunOS-x86/sigqueue -release.external/exechlp-1.0.zip!/SunOS-x86/stat = bin/nativeexecution/SunOS-x86/stat -release.external/exechlp-1.0.zip!/SunOS-x86/unbuffer.so = bin/nativeexecution/SunOS-x86/unbuffer.so -release.external/exechlp-1.0.zip!/SunOS-x86/killall = bin/nativeexecution/SunOS-x86/killall -release.external/exechlp-1.0.zip!/SunOS-x86_64/process_start = bin/nativeexecution/SunOS-x86_64/process_start -release.external/exechlp-1.0.zip!/SunOS-x86_64/pty = bin/nativeexecution/SunOS-x86_64/pty -release.external/exechlp-1.0.zip!/SunOS-x86_64/pty_open = bin/nativeexecution/SunOS-x86_64/pty_open -release.external/exechlp-1.0.zip!/SunOS-x86_64/sigqueue = bin/nativeexecution/SunOS-x86_64/sigqueue -release.external/exechlp-1.0.zip!/SunOS-x86_64/stat = bin/nativeexecution/SunOS-x86_64/stat -release.external/exechlp-1.0.zip!/SunOS-x86_64/unbuffer.so = bin/nativeexecution/SunOS-x86_64/unbuffer.so -release.external/exechlp-1.0.zip!/SunOS-x86_64/killall = bin/nativeexecution/SunOS-x86_64/killall +release.external/exechlp-1.1.zip!/Linux-x86/process_start = bin/nativeexecution/Linux-x86/process_start +release.external/exechlp-1.1.zip!/Linux-x86/pty = bin/nativeexecution/Linux-x86/pty +release.external/exechlp-1.1.zip!/Linux-x86/pty_open = bin/nativeexecution/Linux-x86/pty_open +release.external/exechlp-1.1.zip!/Linux-x86/sigqueue = bin/nativeexecution/Linux-x86/sigqueue +release.external/exechlp-1.1.zip!/Linux-x86/stat = bin/nativeexecution/Linux-x86/stat +release.external/exechlp-1.1.zip!/Linux-x86/unbuffer.so = bin/nativeexecution/Linux-x86/unbuffer.so +release.external/exechlp-1.1.zip!/Linux-x86/killall = bin/nativeexecution/Linux-x86/killall +release.external/exechlp-1.1.zip!/Linux-x86_64/process_start = bin/nativeexecution/Linux-x86_64/process_start +release.external/exechlp-1.1.zip!/Linux-x86_64/pty = bin/nativeexecution/Linux-x86_64/pty +release.external/exechlp-1.1.zip!/Linux-x86_64/pty_open = bin/nativeexecution/Linux-x86_64/pty_open +release.external/exechlp-1.1.zip!/Linux-x86_64/sigqueue = bin/nativeexecution/Linux-x86_64/sigqueue +release.external/exechlp-1.1.zip!/Linux-x86_64/stat = bin/nativeexecution/Linux-x86_64/stat +release.external/exechlp-1.1.zip!/Linux-x86_64/unbuffer.so = bin/nativeexecution/Linux-x86_64/unbuffer.so +release.external/exechlp-1.1.zip!/Linux-x86_64/killall = bin/nativeexecution/Linux-x86_64/killall +release.external/exechlp-1.1.zip!/Windows-x86/process_start = bin/nativeexecution/Windows-x86/process_start +release.external/exechlp-1.1.zip!/Windows-x86/pty = bin/nativeexecution/Windows-x86/pty +release.external/exechlp-1.1.zip!/Windows-x86/pty_open = bin/nativeexecution/Windows-x86/pty_open +release.external/exechlp-1.1.zip!/Windows-x86/sigqueue = bin/nativeexecution/Windows-x86/sigqueue +release.external/exechlp-1.1.zip!/Windows-x86/unbuffer.dll = bin/nativeexecution/Windows-x86/unbuffer.dll +release.external/exechlp-1.1.zip!/Windows-x86/killall = bin/nativeexecution/Windows-x86/killall +release.external/exechlp-1.1.zip!/Windows-x86_64/process_start = bin/nativeexecution/Windows-x86_64/process_start +release.external/exechlp-1.1.zip!/Windows-x86_64/pty = bin/nativeexecution/Windows-x86_64/pty +release.external/exechlp-1.1.zip!/Windows-x86_64/pty_open = bin/nativeexecution/Windows-x86_64/pty_open +release.external/exechlp-1.1.zip!/Windows-x86_64/sigqueue = bin/nativeexecution/Windows-x86_64/sigqueue +release.external/exechlp-1.1.zip!/Windows-x86_64/killall = bin/nativeexecution/Windows-x86_64/killall +release.external/exechlp-1.1.zip!/MacOSX-x86/process_start = bin/nativeexecution/MacOSX-x86/process_start +release.external/exechlp-1.1.zip!/MacOSX-x86/pty = bin/nativeexecution/MacOSX-x86/pty +release.external/exechlp-1.1.zip!/MacOSX-x86/pty_open = bin/nativeexecution/MacOSX-x86/pty_open +release.external/exechlp-1.1.zip!/MacOSX-x86/stat = bin/nativeexecution/MacOSX-x86/stat +release.external/exechlp-1.1.zip!/MacOSX-x86/unbuffer.dylib = bin/nativeexecution/MacOSX-x86/unbuffer.dylib +release.external/exechlp-1.1.zip!/MacOSX-x86/killall = bin/nativeexecution/MacOSX-x86/killall +release.external/exechlp-1.1.zip!/MacOSX-x86_64/process_start = bin/nativeexecution/MacOSX-x86_64/process_start +release.external/exechlp-1.1.zip!/MacOSX-x86_64/pty = bin/nativeexecution/MacOSX-x86_64/pty +release.external/exechlp-1.1.zip!/MacOSX-x86_64/pty_open = bin/nativeexecution/MacOSX-x86_64/pty_open +release.external/exechlp-1.1.zip!/MacOSX-x86_64/stat = bin/nativeexecution/MacOSX-x86_64/stat +release.external/exechlp-1.1.zip!/MacOSX-x86_64/unbuffer.dylib = bin/nativeexecution/MacOSX-x86_64/unbuffer.dylib +release.external/exechlp-1.1.zip!/MacOSX-x86_64/killall = bin/nativeexecution/MacOSX-x86_64/killall +release.external/exechlp-1.1.zip!/SunOS-sparc/privp = bin/nativeexecution/SunOS-sparc/privp +release.external/exechlp-1.1.zip!/SunOS-sparc/process_start = bin/nativeexecution/SunOS-sparc/process_start +release.external/exechlp-1.1.zip!/SunOS-sparc/pty = bin/nativeexecution/SunOS-sparc/pty +release.external/exechlp-1.1.zip!/SunOS-sparc/pty_open = bin/nativeexecution/SunOS-sparc/pty_open +release.external/exechlp-1.1.zip!/SunOS-sparc/sigqueue = bin/nativeexecution/SunOS-sparc/sigqueue +release.external/exechlp-1.1.zip!/SunOS-sparc/stat = bin/nativeexecution/SunOS-sparc/stat +release.external/exechlp-1.1.zip!/SunOS-sparc/unbuffer.so = bin/nativeexecution/SunOS-sparc/unbuffer.so +release.external/exechlp-1.1.zip!/SunOS-sparc/killall = bin/nativeexecution/SunOS-sparc/killall +release.external/exechlp-1.1.zip!/SunOS-sparc_64/privp = bin/nativeexecution/SunOS-sparc_64/privp +release.external/exechlp-1.1.zip!/SunOS-sparc_64/process_start = bin/nativeexecution/SunOS-sparc_64/process_start +release.external/exechlp-1.1.zip!/SunOS-sparc_64/pty = bin/nativeexecution/SunOS-sparc_64/pty +release.external/exechlp-1.1.zip!/SunOS-sparc_64/pty_open = bin/nativeexecution/SunOS-sparc_64/pty_open +release.external/exechlp-1.1.zip!/SunOS-sparc_64/sigqueue = bin/nativeexecution/SunOS-sparc_64/sigqueue +release.external/exechlp-1.1.zip!/SunOS-sparc_64/stat = bin/nativeexecution/SunOS-sparc_64/stat +release.external/exechlp-1.1.zip!/SunOS-sparc_64/unbuffer.so = bin/nativeexecution/SunOS-sparc_64/unbuffer.so +release.external/exechlp-1.1.zip!/SunOS-sparc_64/killall = bin/nativeexecution/SunOS-sparc_64/killall +release.external/exechlp-1.1.zip!/SunOS-x86/privp = bin/nativeexecution/SunOS-x86/privp +release.external/exechlp-1.1.zip!/SunOS-x86/process_start = bin/nativeexecution/SunOS-x86/process_start +release.external/exechlp-1.1.zip!/SunOS-x86/pty = bin/nativeexecution/SunOS-x86/pty +release.external/exechlp-1.1.zip!/SunOS-x86/pty_open = bin/nativeexecution/SunOS-x86/pty_open +release.external/exechlp-1.1.zip!/SunOS-x86/sigqueue = bin/nativeexecution/SunOS-x86/sigqueue +release.external/exechlp-1.1.zip!/SunOS-x86/stat = bin/nativeexecution/SunOS-x86/stat +release.external/exechlp-1.1.zip!/SunOS-x86/unbuffer.so = bin/nativeexecution/SunOS-x86/unbuffer.so +release.external/exechlp-1.1.zip!/SunOS-x86/killall = bin/nativeexecution/SunOS-x86/killall +release.external/exechlp-1.1.zip!/SunOS-x86_64/process_start = bin/nativeexecution/SunOS-x86_64/process_start +release.external/exechlp-1.1.zip!/SunOS-x86_64/pty = bin/nativeexecution/SunOS-x86_64/pty +release.external/exechlp-1.1.zip!/SunOS-x86_64/pty_open = bin/nativeexecution/SunOS-x86_64/pty_open +release.external/exechlp-1.1.zip!/SunOS-x86_64/sigqueue = bin/nativeexecution/SunOS-x86_64/sigqueue +release.external/exechlp-1.1.zip!/SunOS-x86_64/stat = bin/nativeexecution/SunOS-x86_64/stat +release.external/exechlp-1.1.zip!/SunOS-x86_64/unbuffer.so = bin/nativeexecution/SunOS-x86_64/unbuffer.so +release.external/exechlp-1.1.zip!/SunOS-x86_64/killall = bin/nativeexecution/SunOS-x86_64/killall diff --git a/dlight.nativeexecution/nbproject/project.xml b/dlight.nativeexecution/nbproject/project.xml --- a/dlight.nativeexecution/nbproject/project.xml +++ b/dlight.nativeexecution/nbproject/project.xml @@ -54,7 +54,7 @@ 2 - 1.13 + 1.37 @@ -276,6 +276,8 @@ org.netbeans.modules.dlight.uncover org.netbeans.modules.dlight.util org.netbeans.modules.dlight.webstack + org.netbeans.modules.execmerge + org.netbeans.modules.nativeexecution.impl org.netbeans.nativeexecution.terminal org.netbeans.modules.nativeexecution.api org.netbeans.modules.nativeexecution.api.execution diff --git a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/AbstractNativeProcess.java b/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/AbstractNativeProcess.java --- a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/AbstractNativeProcess.java +++ b/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/AbstractNativeProcess.java @@ -47,6 +47,7 @@ import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.Callable; @@ -59,6 +60,9 @@ import java.util.logging.Level; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import org.netbeans.api.extexecution.process.ProcessCharset; +import org.netbeans.api.extexecution.process.ProcessId; +import org.netbeans.api.extexecution.process.ProcessSignal; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.netbeans.modules.nativeexecution.api.HostInfo; import org.netbeans.modules.nativeexecution.api.NativeProcess; @@ -77,8 +81,9 @@ import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; -public abstract class AbstractNativeProcess extends NativeProcess implements ExProcessInfoProvider { +public abstract class AbstractNativeProcess extends NativeProcess implements ExProcessInfoProvider, Lookup.Provider { protected final static java.util.logging.Logger LOG = Logger.getInstance(); private final static Integer PID_TIMEOUT = @@ -96,6 +101,59 @@ // Immutable listeners list. private final Collection listeners; private final Object stateLock; + + private final ProcessId processId = new ProcessId() { + @Override + protected Integer getId() { + try { + return getPID(); + } catch (IOException ex) { + LOG.log(Level.INFO, null, ex); + } + return null; + } + }; + private final ProcessCharset processCharset = new ProcessCharset() { + + @Override + protected Charset getInputCharset() { + return info.getCharset(); + } + + @Override + protected Charset getOutputCharset() { + return info.getCharset(); + } + + @Override + protected Charset getErrorCharset() { + return info.getCharset(); + } + + }; + private final ProcessSignal processSignal = new ProcessSignal() { + + @Override + protected void signal(ProcessSignal.Signal signal) throws IOException { + try { + SignalSupport.signalProcess(execEnv, getPID(), + SignalSupport.translate(signal)); + } catch (UnsupportedOperationException ex) { + throw new IOException(ex); + } + } + + @Override + protected void signalGroup(ProcessSignal.Signal signal) throws IOException { + try { + SignalSupport.signalProcessGroup(execEnv, getPID(), + SignalSupport.translate(signal)); + } catch (UnsupportedOperationException ex) { + throw new IOException(ex); + } + } + }; + private volatile State state; private volatile int pid = 0; private volatile boolean isInterrupted; @@ -142,6 +200,11 @@ listeners = info.getListenersSnapshot(); } + @Override + public final Lookup getLookup() { + return Lookups.fixed(processId, processCharset, processSignal); + } + public final NativeProcess createAndStart() { try { if (hostInfo == null) { diff --git a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/api/util/Signal.java b/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/api/util/Signal.java --- a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/api/util/Signal.java +++ b/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/api/util/Signal.java @@ -41,6 +41,8 @@ */ package org.netbeans.modules.nativeexecution.api.util; +import org.netbeans.api.extexecution.process.ProcessSignal; + /** * * @author ak119685 @@ -48,54 +50,58 @@ public enum Signal { // IDs are taken from signal_iso.h on Solaris - NULL(0), - SIGHUP(1), - SIGINT(2), - SIGQUIT(3), - SIGILL(4), - SIGTRAP(5), - SIGIOT(6), - SIGABRT(6), - SIGEMT(7), - SIGFPE(8), - SIGKILL(9), - SIGBUS(10), - SIGSEGV(11), - SIGSYS(12), - SIGPIPE(13), - SIGALRM(14), - SIGTERM(15), - SIGUSR1(16), - SIGUSR2(17), - SIGCLD(18), - SIGCHLD(18), - SIGPWR(19), - SIGWINCH(20), - SIGURG(21), - SIGPOLL(22), - SIGIO(22), - SIGSTOP(23), - SIGTSTP(24), - SIGCONT(25), - SIGTTIN(26), - SIGTTOU(27), - SIGVTALRM(28), - SIGPROF(29), - SIGXCPU(30), - SIGXFSZ(31), - SIGWAITING(32), - SIGLWP(33), - SIGFREEZE(34), - SIGTHAW(35), - SIGCANCEL(36), - SIGLOST(37), - SIGXRES(38), - SIGJVM1(39), - SIGJVM2(40); + NULL(0, ProcessSignal.Signal.NULL), + SIGHUP(1, ProcessSignal.Signal.SIGHUP), + SIGINT(2, ProcessSignal.Signal.SIGINT), + SIGQUIT(3, ProcessSignal.Signal.SIGQUIT), + SIGILL(4, ProcessSignal.Signal.SIGILL), + SIGTRAP(5, ProcessSignal.Signal.SIGTRAP), + SIGIOT(6, null), + SIGABRT(6, ProcessSignal.Signal.SIGABRT), + SIGEMT(7, ProcessSignal.Signal.SIGEMT), + SIGFPE(8, ProcessSignal.Signal.SIGFPE), + SIGKILL(9, ProcessSignal.Signal.SIGKILL), + SIGBUS(10, ProcessSignal.Signal.SIGBUS), + SIGSEGV(11, ProcessSignal.Signal.SIGSEGV), + SIGSYS(12, ProcessSignal.Signal.SIGSYS), + SIGPIPE(13, ProcessSignal.Signal.SIGPIPE), + SIGALRM(14, ProcessSignal.Signal.SIGALRM), + SIGTERM(15, ProcessSignal.Signal.SIGTERM), + SIGUSR1(16, ProcessSignal.Signal.SIGUSR1), + SIGUSR2(17, ProcessSignal.Signal.SIGUSR2), + SIGCLD(18, null), + SIGCHLD(18, ProcessSignal.Signal.SIGCHLD), + SIGPWR(19, ProcessSignal.Signal.SIGPWR), + SIGWINCH(20, ProcessSignal.Signal.SIGWINCH), + SIGURG(21, ProcessSignal.Signal.SIGURG), + SIGPOLL(22, ProcessSignal.Signal.SIGPOLL), + SIGIO(22, null), + SIGSTOP(23, ProcessSignal.Signal.SIGSTOP), + SIGTSTP(24, ProcessSignal.Signal.SIGTSTP), + SIGCONT(25, ProcessSignal.Signal.SIGCONT), + SIGTTIN(26, ProcessSignal.Signal.SIGTTIN), + SIGTTOU(27, ProcessSignal.Signal.SIGTTOU), + SIGVTALRM(28, ProcessSignal.Signal.SIGVTALRM), + SIGPROF(29, ProcessSignal.Signal.SIGPROF), + SIGXCPU(30, ProcessSignal.Signal.SIGXCPU), + SIGXFSZ(31, null), + SIGWAITING(32, ProcessSignal.Signal.SIGWAITING), + SIGLWP(33, ProcessSignal.Signal.SIGLWP), + SIGFREEZE(34, ProcessSignal.Signal.SIGFREEZE), + SIGTHAW(35, ProcessSignal.Signal.SIGTHAW), + SIGCANCEL(36, ProcessSignal.Signal.SIGCANCEL), + SIGLOST(37, ProcessSignal.Signal.SIGLOST), + SIGXRES(38, ProcessSignal.Signal.SIGXRES), + SIGJVM1(39, ProcessSignal.Signal.SIGJVM1), + SIGJVM2(40, null); + private final int id; - private Signal(int id) { + private final ProcessSignal.Signal apiSignal; + + private Signal(int id, ProcessSignal.Signal apiSignal) { this.id = id; + this.apiSignal = apiSignal; } /* @@ -107,4 +113,8 @@ int getID() { return id; } + + public ProcessSignal.Signal getApiSignal() { + return apiSignal; + } } diff --git a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/signals/SignalSupport.java b/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/signals/SignalSupport.java --- a/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/signals/SignalSupport.java +++ b/dlight.nativeexecution/src/org/netbeans/modules/nativeexecution/signals/SignalSupport.java @@ -42,6 +42,9 @@ package org.netbeans.modules.nativeexecution.signals; import java.util.Collection; +import java.util.EnumMap; +import java.util.Map; +import org.netbeans.api.extexecution.process.ProcessSignal; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.netbeans.modules.nativeexecution.api.util.Signal; import org.openide.util.Lookup; @@ -140,4 +143,19 @@ return result; } + + private static Map translation; + + public synchronized static Signal translate(ProcessSignal.Signal signal) { + if (translation == null) { + translation = new EnumMap(ProcessSignal.Signal.class); + for (Signal sig : Signal.values()) { + ProcessSignal.Signal api = sig.getApiSignal(); + if (api != null) { + translation.put(api, sig); + } + } + } + return translation.get(signal); + } } diff --git a/dlight.nativeexecution/tools/pty/Makefile b/dlight.nativeexecution/tools/pty/Makefile --- a/dlight.nativeexecution/tools/pty/Makefile +++ b/dlight.nativeexecution/tools/pty/Makefile @@ -17,6 +17,7 @@ $(ROOT_DIR)/src/options.c \ $(ROOT_DIR)/src/pty_fork.c \ $(ROOT_DIR)/src/pty.c \ + $(ROOT_DIR)/src/stty.c \ $(ROOT_DIR)/src/util.c SRC_DIRS=\ @@ -30,7 +31,6 @@ EXEC=$(DIST_DIR)/$(PNAME) - CF_COMMON = --std=c99 -s -O2 CF_Linux-x86 = $(CF_COMMON) -DLINUX -D_GNU_SOURCE -D_XOPEN_SOURCE=700 -m32 CF_Linux-x86_64 = $(CF_COMMON) -DLINUX -D_GNU_SOURCE -D_XOPEN_SOURCE=700 -m64 @@ -40,8 +40,8 @@ CF_SunOS-sparc_64 = $(CF_COMMON) -DSOLARIS -D__EXTENSIONS__ -m64 CF_SunOS-x86 = $(CF_COMMON) -DSOLARIS -D__EXTENSIONS__ -m32 CF_SunOS-x86_64 = $(CF_COMMON) -DSOLARIS -D__EXTENSIONS__ -m64 -CF_Windows-x86 = $(CF_COMMON) -DWINDOWS -m32 -CF_Windows-x86_64 = $(CF_COMMON) -DWINDOWS -m32 +CF_Windows-x86 = $(CF_COMMON) -DWINDOWS -m32 --std=gnu99 +CF_Windows-x86_64 = $(CF_COMMON) -DWINDOWS -m32 --std=gnu99 LF_Windows-x86 = --static-libgcc LF_Windows-x86_64 = --static-libgcc diff --git a/dlight.nativeexecution/tools/pty/src/options.c b/dlight.nativeexecution/tools/pty/src/options.c --- a/dlight.nativeexecution/tools/pty/src/options.c +++ b/dlight.nativeexecution/tools/pty/src/options.c @@ -35,6 +35,9 @@ } opts->envfile = argv[idx]; return argc; // pretend that everything is parsed ... + } else if (strcmp(argv[idx], "--resize") == 0) { + opts->resize = 1; + nopt += 1; } else if (strcmp(argv[idx], "--") == 0) { idx++; nopt += 2; diff --git a/dlight.nativeexecution/tools/pty/src/options.h b/dlight.nativeexecution/tools/pty/src/options.h --- a/dlight.nativeexecution/tools/pty/src/options.h +++ b/dlight.nativeexecution/tools/pty/src/options.h @@ -20,6 +20,7 @@ int set_erase_key; int redirect_error; int waitSignal; + int resize; char *pty; char *wdir; const char *envfile; diff --git a/dlight.nativeexecution/tools/pty/src/pty.c b/dlight.nativeexecution/tools/pty/src/pty.c --- a/dlight.nativeexecution/tools/pty/src/pty.c +++ b/dlight.nativeexecution/tools/pty/src/pty.c @@ -79,9 +79,10 @@ // --dumpenv record current environment in a specified file // --report record process' and exit status information into // specified file + // --resize send TIOCSWINSZ to a terminal with the specified sizes err_quit("\n\n" - "usage: %s [-e] [--no-pty] [-w] [-p pts_name] [--set-erase-key]\n" + "usage: %s\t[-e] [--no-pty] [-w] [-p pts_name] [--set-erase-key]\n" "\t\t[--readenv env_file] [[--env NAME=VALUE] ...] [--dir dir]\n" "\t\t[--redirect-error] [--report report_file] program [ arg ... ]\n\n" "\t-e\t\t turn echoing off\n" @@ -92,9 +93,12 @@ "\t--dir\t\t change working directory for starting process\n" "\t--redirect-error redirect stderror to stdout for starting process (makes sense if --no-pty only)\n" "\t--report\t record process' and exit status information into specified file\n\n" - "usage: %s --dumpenv env_file\n" - "\t--dumpenv\t dump environment to a file\n" - , progname, progname); + "usage: %s\t--dumpenv env_file\n" + "\t--dumpenv\t dump environment to a file\n\n" + "usage: %s\t--resize -p pts_name cols,rows,xpixels,ypixels\n" + "\t--resize\t send TIOCSWINSZ to a terminal with the specified sizes\n" + "\t\t\t Zero or not specified size means no change\n" + , progname, progname, progname); exit(-1); } @@ -107,6 +111,10 @@ dup2(STDOUT_FILENO, STDERR_FILENO); } + if (params.resize) { + return stty_winsz(params.pty, argv[0]); + } + if (params.nopty == 0) { if (params.pty != NULL) { pid = pty_fork1(params.pty); diff --git a/dlight.nativeexecution/tools/pty/src/pty.h b/dlight.nativeexecution/tools/pty/src/pty.h --- a/dlight.nativeexecution/tools/pty/src/pty.h +++ b/dlight.nativeexecution/tools/pty/src/pty.h @@ -10,6 +10,7 @@ #include "error.h" #include "util.h" #include "options.h" +#include "stty.h" #include #include #include diff --git a/dlight.nativeexecution/tools/pty/src/stty.c b/dlight.nativeexecution/tools/pty/src/stty.c new file mode 100644 --- /dev/null +++ b/dlight.nativeexecution/tools/pty/src/stty.c @@ -0,0 +1,58 @@ +#include "stty.h" +#include "error.h" +#include +#include +#include +#include + +#if defined (__CYGWIN__) || defined (LINUX) +#include +#endif + +#if defined (__CYGWIN__) +#include +#else +#include +#endif + +int stty_winsz(const char* pts, const char* sz) { + if (pts == NULL) { + err_quit("pts must be specified when --resize is used"); + } + char* sizes = strdup(sz); + if (sizes == (char *) NULL) { + err_sys("strdup error"); + } + char *t; + char *lasts; + struct winsize wsize, orig; + + int pty_fd; + if ((pty_fd = open(pts, O_RDWR)) == -1) { + err_sys("ERROR cannot open pty \"%s\" -- %s\n", pts, strerror(errno)); + } + + if (!isatty(pty_fd)) { + err_quit("Not a terminal: %s\n", pts); + } + + ioctl(pty_fd, TIOCGWINSZ, &orig); + + t = strtok_r(sizes, ",", &lasts); + wsize.ws_col = t == NULL ? orig.ws_col : atoi(t); + if (wsize.ws_col == 0) wsize.ws_col = orig.ws_col; + + t = strtok_r(NULL, ",", &lasts); + wsize.ws_row = t == NULL ? orig.ws_row : atoi(t); + if (wsize.ws_row == 0) wsize.ws_row = orig.ws_row; + + t = strtok_r(NULL, ",", &lasts); + wsize.ws_xpixel = t == NULL ? orig.ws_xpixel : atoi(t); + if (wsize.ws_xpixel == 0) wsize.ws_xpixel = orig.ws_xpixel; + + t = strtok_r(NULL, ",", &lasts); + wsize.ws_ypixel = t == NULL ? orig.ws_ypixel : atoi(t); + if (wsize.ws_ypixel == 0) wsize.ws_ypixel = orig.ws_ypixel; + + return ioctl(pty_fd, TIOCSWINSZ, &wsize); +} diff --git a/dlight.nativeexecution/tools/pty/src/stty.h b/dlight.nativeexecution/tools/pty/src/stty.h new file mode 100644 --- /dev/null +++ b/dlight.nativeexecution/tools/pty/src/stty.h @@ -0,0 +1,20 @@ +/* + * File: stty.h + * Author: akrasny + */ + +#ifndef STTY_H +#define STTY_H + +#ifdef __cplusplus +extern "C" { +#endif + + int stty_winsz(const char*, const char*); + +#ifdef __cplusplus +} +#endif + +#endif /* STTY_H */ + diff --git a/dlight.remote.impl/src/org/netbeans/modules/remote/impl/fileoperations/spi/FileOperationsProvider.java b/dlight.remote.impl/src/org/netbeans/modules/remote/impl/fileoperations/spi/FileOperationsProvider.java --- a/dlight.remote.impl/src/org/netbeans/modules/remote/impl/fileoperations/spi/FileOperationsProvider.java +++ b/dlight.remote.impl/src/org/netbeans/modules/remote/impl/fileoperations/spi/FileOperationsProvider.java @@ -49,6 +49,7 @@ import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import org.netbeans.api.extexecution.Environment; import org.netbeans.api.extexecution.ProcessBuilder; import org.netbeans.modules.dlight.libs.common.PathUtilities; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; @@ -62,8 +63,11 @@ import org.netbeans.modules.remote.impl.fs.RemoteFileSystem; import org.netbeans.modules.remote.impl.fs.RemoteFileSystemManager; import org.netbeans.modules.remote.impl.fs.RemoteFileUrlMapper; +import org.netbeans.spi.extexecution.EnvironmentFactory; +import org.netbeans.spi.extexecution.EnvironmentImplementation; import org.netbeans.spi.extexecution.ProcessBuilderFactory; -import org.netbeans.spi.extexecution.ProcessBuilderImplementation; +import org.netbeans.spi.extexecution.ProcessBuilderImplementation2; +import org.netbeans.spi.extexecution.ProcessParameters; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileSystem; import org.openide.util.Lookup; @@ -378,7 +382,7 @@ } protected ProcessBuilder createProcessBuilder(FileProxyO file) { - return ProcessBuilderFactory.createProcessBuilder(new ProcessBuilderImplementationImpl(env), "RFS Process Builder"); // NOI18N + return ProcessBuilderFactory.createProcessBuilder(new ProcessBuilderImplementationImpl2(env), "RFS Process Builder"); // NOI18N } protected void refreshFor(FileProxyO ... files) { @@ -462,23 +466,33 @@ return defaultProvider; } - private static final class ProcessBuilderImplementationImpl implements ProcessBuilderImplementation { + private static final class ProcessBuilderImplementationImpl2 implements ProcessBuilderImplementation2 { private final ExecutionEnvironment env; - private ProcessBuilderImplementationImpl(ExecutionEnvironment env) { + private final NativeProcessBuilder pb; + private final Environment environment; + private ProcessBuilderImplementationImpl2(ExecutionEnvironment env) { this.env = env; + this.pb = NativeProcessBuilder.newProcessBuilder(env); + this.environment = EnvironmentFactory.createEnvironment( + new EnvironmentImplementationImpl(pb.getEnvironment())); } @Override - public Process createProcess(String executable, String workingDirectory, List arguments, List paths, Map environment, boolean redirectErrorStream) throws IOException { - NativeProcessBuilder pb = NativeProcessBuilder.newProcessBuilder(env); - pb.setExecutable(executable).setWorkingDirectory(workingDirectory).setArguments(arguments.toArray(new String[arguments.size()])); - MacroMap mm = MacroMap.forExecEnv(env); - mm.putAll(environment); - pb.getEnvironment().putAll(mm); - for(String path : paths) { - pb.getEnvironment().appendPathVariable("PATH", path); // NOI18N - } - if (redirectErrorStream) { + public Environment getEnvironment() { + return this.environment; + } + + @Override + public Lookup getLookup() { + return Lookup.EMPTY; + } + + @Override + public Process createProcess(ProcessParameters parameters) throws IOException { + pb.setExecutable(parameters.getExecutable()) + .setWorkingDirectory(parameters.getWorkingDirectory()) + .setArguments(parameters.getArguments().toArray(new String[parameters.getArguments().size()])); + if (parameters.isRedirectErrorStream()) { pb.redirectError(); } return pb.call(); @@ -489,6 +503,43 @@ return env.getDisplayName(); } } + + private static final class EnvironmentImplementationImpl implements EnvironmentImplementation { + private final MacroMap mm; + public EnvironmentImplementationImpl(MacroMap mm) { + this.mm = mm; + } + + @Override + public String getVariable(String name) { + return mm.get(name); + } + + @Override + public void appendPath(String name, String value) { + mm.appendPathVariable(name, value); + } + + @Override + public void prependPath(String name, String value) { + mm.prependPathVariable(name, value); + } + + @Override + public void setVariable(String name, String value) { + mm.put(name, value); + } + + @Override + public void removeVariable(String name) { + mm.remove(name); + } + + @Override + public Map values() { + return mm.toMap(); + } + } public interface FileProxyO { diff --git a/dlight.remote/manifest.mf b/dlight.remote/manifest.mf --- a/dlight.remote/manifest.mf +++ b/dlight.remote/manifest.mf @@ -3,5 +3,5 @@ OpenIDE-Module: org.netbeans.modules.dlight.remote OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/remote/resources/Bundle.properties -OpenIDE-Module-Needs: org.netbeans.modules.dlight.remote.spi.RemoteProviderToken +OpenIDE-Module-Needs: org.netbeans.modules.dlight.remote.spi.RemoteProviderToken, ConnectionService.localhost, ConnectionService.ssh OpenIDE-Module-Recommends: org.netbeans.modules.remotefs.versioning diff --git a/dlight.remote/nbproject/project.xml b/dlight.remote/nbproject/project.xml --- a/dlight.remote/nbproject/project.xml +++ b/dlight.remote/nbproject/project.xml @@ -15,6 +15,14 @@ + org.netbeans.modules.cnd.execution + + + + 1.0 + + + org.netbeans.modules.dlight.libs.common @@ -31,6 +39,14 @@ + org.netbeans.modules.execmerge + + + + 1.0 + + + org.openide.awt diff --git a/dlight.remote/src/org/netbeans/modules/remote/api/ui/FileChooserBuilder.java b/dlight.remote/src/org/netbeans/modules/remote/api/ui/FileChooserBuilder.java --- a/dlight.remote/src/org/netbeans/modules/remote/api/ui/FileChooserBuilder.java +++ b/dlight.remote/src/org/netbeans/modules/remote/api/ui/FileChooserBuilder.java @@ -63,6 +63,7 @@ import javax.swing.filechooser.FileSystemView; import javax.swing.filechooser.FileView; import javax.swing.plaf.FileChooserUI; +import org.netbeans.modules.cnd.execution.api.Connection; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory; import org.netbeans.modules.remote.spi.FileSystemProvider; @@ -95,7 +96,9 @@ public abstract void setCurrentDirectory(FileObject dir); + public abstract FileObject getSelectedFileObject(); + public abstract FileObject[] getSelectedFileObjects(); @Override @@ -139,10 +142,17 @@ private static final String readOnlyKey = "FileChooser.readOnly"; // NOI18N private final ExecutionEnvironment env; + private final Connection connection; private Preferences forModule; public FileChooserBuilder(ExecutionEnvironment env) { this.env = env; + this.connection = null; + } + + public FileChooserBuilder(Connection connection) { + this.connection = connection; + this.env = null; } public JFileChooserEx createFileChooser(Callable selectedPath) { @@ -291,6 +301,7 @@ private static class RemoteFileChooserImpl extends JFileChooserEx implements PropertyChangeListener { + private final Preferences forModule; private final ExecutionEnvironment env; @@ -411,7 +422,7 @@ String path = getSelectedFile().getAbsolutePath(); if (forModule != null) { String envID = ExecutionEnvironmentFactory.toUniqueID(env); - forModule.put("FileChooserPath"+envID, path); // NOI18N + forModule.put("FileChooserPath" + envID, path); // NOI18N } } } @@ -420,6 +431,7 @@ } private static class CustomFileView extends FileView { + final FileSystemView view; public CustomFileView(FileSystemView view) { diff --git a/dlight.remote/src/org/netbeans/modules/remote/spi/FileSystemProvider.java b/dlight.remote/src/org/netbeans/modules/remote/spi/FileSystemProvider.java --- a/dlight.remote/src/org/netbeans/modules/remote/spi/FileSystemProvider.java +++ b/dlight.remote/src/org/netbeans/modules/remote/spi/FileSystemProvider.java @@ -45,10 +45,13 @@ import java.io.File; import java.io.FileFilter; import java.io.IOException; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.Callable; import java.util.logging.Level; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.execmerge.ConnectionUtils; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory; import org.netbeans.modules.remote.support.RemoteLogger; @@ -84,6 +87,7 @@ return getFileSystem(env, "/"); //NOI18N } + @Deprecated public static ExecutionEnvironment getExecutionEnvironment(FileSystem fileSystem) { for (FileSystemProviderImplementation provider : ALL_PROVIDERS) { if (provider.isMine(fileSystem)) { @@ -93,6 +97,7 @@ return ExecutionEnvironmentFactory.getLocal(); } + @Deprecated public static ExecutionEnvironment getExecutionEnvironment(FileObject fileObject) { try { return getExecutionEnvironment(fileObject.getFileSystem()); @@ -102,6 +107,14 @@ return ExecutionEnvironmentFactory.getLocal(); } + public static Connection getConnection(FileObject fileObject) { + return ConnectionUtils.ExecutionEnvironment2Connection(getExecutionEnvironment(fileObject)); + } + + public static Connection getConnection(FileSystem fileSystem) { + return ConnectionUtils.ExecutionEnvironment2Connection(getExecutionEnvironment(fileSystem)); + } + public static FileSystem getFileSystem(ExecutionEnvironment env, String root) { for (FileSystemProviderImplementation provider : ALL_PROVIDERS) { if (provider.isMine(env)) { @@ -190,6 +203,18 @@ return FileUtil.toFileObject(FileUtil.normalizeFile(new File(absPath))); } + public static FileObject getFileObject(Connection connection, String absPath) { + return getFileObject(Connection2ExecutionEnvironment(connection), absPath); + } + + private static ExecutionEnvironment Connection2ExecutionEnvironment(Connection connection) { + URI uri = connection.getURI(); + if ("localhost".equals(uri.getScheme())) { // NOI18N + return ExecutionEnvironmentFactory.getLocal(); + } + return ExecutionEnvironmentFactory.createNew(uri.getUserInfo(), uri.getHost()); + } + public static FileObject getCanonicalFileObject(FileObject fileObject) throws IOException { for (FileSystemProviderImplementation provider : ALL_PROVIDERS) { if (provider.isMine(fileObject)) { diff --git a/dlight.terminal/manifest.mf b/dlight.terminal/manifest.mf --- a/dlight.terminal/manifest.mf +++ b/dlight.terminal/manifest.mf @@ -4,4 +4,5 @@ OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module-Layer: org/netbeans/modules/dlight/terminal/layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/dlight/terminal/Bundle.properties +OpenIDE-Module-Recommends: org.netbeans.modules.dlight.spi.terminal.ConnectionProvider diff --git a/dlight.terminal/nbproject/project.xml b/dlight.terminal/nbproject/project.xml --- a/dlight.terminal/nbproject/project.xml +++ b/dlight.terminal/nbproject/project.xml @@ -23,11 +23,29 @@ - org.netbeans.modules.dlight.nativeexecution + org.netbeans.modules.autoupdate.ui - 1.10.3 + 1.35 + + + + org.netbeans.modules.extexecution + + + + 2 + 1.37 + + + + org.netbeans.modules.options.api + + + + 1 + 1.32 @@ -105,9 +123,12 @@ com.sun.tools.ide.analysis.discover com.sun.tools.ide.analysis.previse com.sun.tools.ide.analysis.tha + org.netbeans.modules.cnd.execution org.netbeans.modules.cnd.makeproject org.netbeans.modules.cnd.remote + org.netbeans.modules.nativeexecution.impl org.netbeans.modules.dlight.api.terminal + org.netbeans.modules.dlight.spi.terminal diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/api/terminal/ProcessTerminal.java b/dlight.terminal/src/org/netbeans/modules/dlight/api/terminal/ProcessTerminal.java new file mode 100644 --- /dev/null +++ b/dlight.terminal/src/org/netbeans/modules/dlight/api/terminal/ProcessTerminal.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.dlight.api.terminal; + +import java.awt.Dimension; +import java.util.concurrent.atomic.AtomicReference; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.util.RequestProcessor.Task; + +/** + * + * @author akrasny + */ +public abstract class ProcessTerminal { + + private static final RequestProcessor RP = new RequestProcessor("ProcessTerminal resize", 1); // NOI18N + private final AtomicReference sizeRef = new AtomicReference(); + private final AtomicReference pixelSizeRef = new AtomicReference(); + private final Task resizeTask = RP.create(new Runnable() { + @Override + public void run() { + synchronized (resizeTask) { + ttyResized(sizeRef.get(), pixelSizeRef.get()); + } + } + }, true); + + private static ProcessTerminal find(Process process) { + if (process instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) process; + return p.getLookup().lookup(ProcessTerminal.class); + } + return null; + } + + public static boolean isSupported(Process process) { + return find(process) != null; + } + + public static void ttyResized(Process process, Dimension size, Dimension pixelSize) { + ProcessTerminal termInfo = find(process); + if (termInfo != null) { + synchronized (termInfo.resizeTask) { + termInfo.sizeRef.set(size); + termInfo.pixelSizeRef.set(pixelSize); + } + termInfo.resizeTask.schedule(500); + } + } + + protected abstract void ttyResized(Dimension size, Dimension pixelSize); +} diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/api/terminal/TerminalSupport.java b/dlight.terminal/src/org/netbeans/modules/dlight/api/terminal/TerminalSupport.java --- a/dlight.terminal/src/org/netbeans/modules/dlight/api/terminal/TerminalSupport.java +++ b/dlight.terminal/src/org/netbeans/modules/dlight/api/terminal/TerminalSupport.java @@ -46,7 +46,6 @@ import javax.swing.Action; import org.netbeans.modules.dlight.terminal.action.TerminalSupportImpl; import org.netbeans.modules.dlight.terminal.ui.TerminalContainerTopComponent; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.openide.windows.IOContainer; /** @@ -63,8 +62,8 @@ * @param ioContainer * @param env */ - public static void openTerminal(IOContainer ioContainer, String termTitle, ExecutionEnvironment env) { - TerminalSupportImpl.openTerminalImpl(ioContainer, termTitle, env, null, false); + public static void openTerminal(IOContainer ioContainer, String termTitle, org.netbeans.api.extexecution.ProcessBuilder.Provider provider) { + TerminalSupportImpl.openTerminalImpl(ioContainer, termTitle, provider, null, false); } /** @@ -72,15 +71,15 @@ * @param ioContainer * @param env */ - public static void openTerminal(IOContainer ioContainer, String termTitle, ExecutionEnvironment env, String dir) { - TerminalSupportImpl.openTerminalImpl(ioContainer, termTitle, env, dir, false); + public static void openTerminal(IOContainer ioContainer, String termTitle, org.netbeans.api.extexecution.ProcessBuilder.Provider provider, String dir) { + TerminalSupportImpl.openTerminalImpl(ioContainer, termTitle, provider, dir, false); } /** * opens terminal tab in default terminals container and change dir into specified directory * @param env */ - public static void openTerminal(String termTitle, ExecutionEnvironment env, String dir) { + public static void openTerminal(String termTitle, org.netbeans.api.extexecution.ProcessBuilder.Provider provider, String dir) { final TerminalContainerTopComponent instance = TerminalContainerTopComponent.findInstance(); Object prev = instance.getClientProperty(TerminalContainerTopComponent.AUTO_OPEN_LOCAL_PROPERTY); instance.putClientProperty(TerminalContainerTopComponent.AUTO_OPEN_LOCAL_PROPERTY, Boolean.FALSE); @@ -88,7 +87,7 @@ instance.open(); instance.requestActive(); IOContainer ioContainer = instance.getIOContainer(); - TerminalSupportImpl.openTerminalImpl(ioContainer, termTitle, env, dir, false); + TerminalSupportImpl.openTerminalImpl(ioContainer, termTitle, provider, dir, false); } finally { instance.putClientProperty(TerminalContainerTopComponent.AUTO_OPEN_LOCAL_PROPERTY, prev); } diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/ConnectionProvider.java b/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/ConnectionProvider.java new file mode 100644 --- /dev/null +++ b/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/ConnectionProvider.java @@ -0,0 +1,57 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.modules.dlight.spi.terminal; + +import java.io.IOException; +import java.net.URI; +import org.netbeans.api.extexecution.ProcessBuilder; + +/** + * + * @author Petr Hejl + */ +public interface ConnectionProvider { + + ProcessBuilder.Provider getConnection(URI uri) throws IOException; + + URI getLocal(); +} diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/ShellConfiguration.java b/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/ShellConfiguration.java new file mode 100644 --- /dev/null +++ b/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/ShellConfiguration.java @@ -0,0 +1,52 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.modules.dlight.spi.terminal; + +/** + * + * @author Petr Hejl + */ +public interface ShellConfiguration { + + String getShell(); + +} diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/TerminalConfiguration.java b/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/TerminalConfiguration.java new file mode 100644 --- /dev/null +++ b/dlight.terminal/src/org/netbeans/modules/dlight/spi/terminal/TerminalConfiguration.java @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.modules.dlight.spi.terminal; + +/** + * + * @author Petr Hejl + */ +public interface TerminalConfiguration { + + boolean isTerminalMode(); + + void setTerminalMode(boolean terminalMode); + + String getEmulation(); + + void setEmulation(String emulation); + +} diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/Bundle.properties b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/Bundle.properties --- a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/Bundle.properties +++ b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/Bundle.properties @@ -1,7 +1,6 @@ CTL_LocalTerminal=Terminal CTL_RemoteTerminalAction=Remote Terminal RemoteConnectionTitle=SSH Connection -TerminalAction.FailedToStart.text=Failed to start terminal: {0} RemoteTerminalShortDescr=Create New Remote Terminal Tab LocalTerminalShortDescr=Create New Local Terminal Tab CTL_ShowTerminalAction=T&erminal diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/LocalTerminalAction.java b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/LocalTerminalAction.java --- a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/LocalTerminalAction.java +++ b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/LocalTerminalAction.java @@ -41,13 +41,14 @@ */ package org.netbeans.modules.dlight.terminal.action; +import java.net.URI; +import org.netbeans.modules.dlight.spi.terminal.ConnectionProvider; import org.netbeans.modules.dlight.terminal.ui.TerminalContainerTopComponent; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionRegistration; import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; import org.openide.util.NbBundle; /** @@ -55,7 +56,7 @@ * @author Vladimir Voskresensky */ @ActionID(id = "LocalTerminalAction", category = "Window") -@ActionRegistration(iconInMenu = true, displayName = "#CTL_LocalTerminal", iconBase = "org/netbeans/modules/dlight/terminal/action/local_term.png") +@ActionRegistration(iconInMenu = true, lazy = false, displayName = "#CTL_LocalTerminal", iconBase = "org/netbeans/modules/dlight/terminal/action/local_term.png") @ActionReference(path = TerminalAction.TERMINAL_ACTIONS_PATH, name = "org-netbeans-modules-dlight-terminal-action-LocalTerminalAction", position = 100) public final class LocalTerminalAction extends TerminalAction { @@ -65,7 +66,7 @@ } @Override - protected ExecutionEnvironment getEnvironment() { - return ExecutionEnvironmentFactory.getLocal(); + protected URI getConnectionURI() { + return Lookup.getDefault().lookup(ConnectionProvider.class).getLocal(); } } diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/RemoteTerminalAction.java b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/RemoteTerminalAction.java --- a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/RemoteTerminalAction.java +++ b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/RemoteTerminalAction.java @@ -42,14 +42,16 @@ package org.netbeans.modules.dlight.terminal.action; import java.awt.Dialog; +import java.net.URI; +import org.netbeans.modules.dlight.spi.terminal.ConnectionProvider; import org.netbeans.modules.dlight.terminal.ui.RemoteInfoDialog; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.awt.ActionRegistration; import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; import org.openide.util.NbBundle; /** @@ -57,7 +59,7 @@ * @author Vladimir Voskresensky */ @ActionID(id = "RemoteTerminalAction", category = "Window") -@ActionRegistration(iconInMenu = true, displayName = "#CTL_RemoteTerminalAction", iconBase = "org/netbeans/modules/dlight/terminal/action/remote_term.png") +@ActionRegistration(iconInMenu = true, lazy = false, displayName = "#CTL_RemoteTerminalAction", iconBase = "org/netbeans/modules/dlight/terminal/action/remote_term.png") @ActionReference(path = TerminalAction.TERMINAL_ACTIONS_PATH, name = "org-netbeans-modules-dlight-terminal-action-RemoteTerminalAction", position = 200) public final class RemoteTerminalAction extends TerminalAction { @@ -70,7 +72,7 @@ } @Override - protected ExecutionEnvironment getEnvironment() { + protected URI getConnectionURI() { String title = NbBundle.getMessage(RemoteTerminalAction.class, "RemoteConnectionTitle"); cfgPanel.init(); DialogDescriptor dd = new DialogDescriptor(cfgPanel, title, // NOI18N @@ -78,7 +80,7 @@ DialogDescriptor.OK_OPTION, null); Dialog cfgDialog = DialogDisplayer.getDefault().createDialog(dd); - + try { cfgDialog.setVisible(true); } catch (Throwable th) { @@ -94,7 +96,6 @@ return null; } - final ExecutionEnvironment env = cfgPanel.getExecutionEnvironment(); - return env; + return cfgPanel.getConnectionURI(); } } diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/ShowTerminalAction.java b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/ShowTerminalAction.java --- a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/ShowTerminalAction.java +++ b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/ShowTerminalAction.java @@ -59,6 +59,9 @@ public class ShowTerminalAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { + if (!TerminalAction.checkInstalled()) { + return; + } final TerminalContainerTopComponent instance = TerminalContainerTopComponent.findInstance(); instance.open(); instance.requestActive(); diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalAction.java b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalAction.java --- a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalAction.java +++ b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalAction.java @@ -43,11 +43,22 @@ import java.awt.Component; import java.awt.event.ActionEvent; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.NoRouteToHostException; +import java.net.URI; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; +import org.netbeans.modules.autoupdate.ui.api.PluginManager; +import org.netbeans.modules.dlight.spi.terminal.ConnectionProvider; import org.netbeans.modules.dlight.terminal.ui.TerminalContainerTopComponent; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.openide.util.actions.Presenter; import org.openide.windows.IOContainer; import org.openide.windows.IOProvider; @@ -60,6 +71,8 @@ public static final String TERMINAL_ACTIONS_PATH = "Terminal/Actions"; // NOI18N + private static final String NATIVE_EXECUTION = "org.netbeans.modules.cnd.execution"; // NOI18N + public TerminalAction(String name, String descr, ImageIcon icon) { putValue(Action.NAME, name); putValue(Action.SMALL_ICON, icon); @@ -67,17 +80,50 @@ } @Override + @Messages({ + "# {0} - host", + "UnreachableHostError=Host {0} cannot be reached. Please verify proxy settings." + }) public void actionPerformed(final ActionEvent e) { + if (!checkInstalled()) { + return; + } + final TerminalContainerTopComponent instance = TerminalContainerTopComponent.findInstance(); instance.open(); instance.requestActive(); final IOContainer ioContainer = instance.getIOContainer(); final IOProvider term = IOProvider.get("Terminal"); // NOI18N - if (term != null) { - final ExecutionEnvironment env = getEnvironment(); - if (env != null) { - TerminalSupportImpl.openTerminalImpl(ioContainer, env.getDisplayName(), env, null, TerminalContainerTopComponent.SILENT_MODE_COMMAND.equals(e.getActionCommand())); - } + assert term != null; + + boolean silentMode = TerminalContainerTopComponent.SILENT_MODE_COMMAND.equals(e.getActionCommand()); + + final URI connectionURI = getConnectionURI(); + if (connectionURI == null) { + return; + } + + try { + ConnectionProvider provider = Lookup.getDefault().lookup(ConnectionProvider.class); + + // XXX disable action when lookup empty + final org.netbeans.api.extexecution.ProcessBuilder.Provider pbp = + provider.getConnection(connectionURI); + + TerminalSupportImpl.openTerminalImpl( + ioContainer, + null, + pbp, + null, /* dir */ + silentMode); + } catch (InterruptedIOException ex) { + return; + } catch (NoRouteToHostException ex) { + DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message( + Bundle.UnreachableHostError(connectionURI.getHost()), + NotifyDescriptor.ERROR_MESSAGE)); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); } } @@ -86,5 +132,22 @@ return TerminalSupportImpl.getToolbarPresenter(this); } - protected abstract ExecutionEnvironment getEnvironment(); + protected abstract URI getConnectionURI(); + + @NbBundle.Messages({ + "module install_title=Native Execution API installation", + "module_install_question=Terminal requires Native Execution API support. Do you want to install it now?", + "native_execution=Native Execution API" + }) + public static boolean checkInstalled() { + if (Lookup.getDefault().lookup(ConnectionProvider.class) != null) { + return true; + } + NotifyDescriptor d = new NotifyDescriptor.Confirmation(Bundle.module_install_question(), + Bundle.module_install_title(), NotifyDescriptor.YES_NO_OPTION); + if (DialogDisplayer.getDefault().notify(d) == NotifyDescriptor.YES_OPTION) { + return null == PluginManager.installSingle(NATIVE_EXECUTION, Bundle.native_execution()); + } + return false; + } } diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalSupportImpl.java b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalSupportImpl.java --- a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalSupportImpl.java +++ b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/action/TerminalSupportImpl.java @@ -39,44 +39,27 @@ * * Portions Copyrighted 2011 Sun Microsystems, Inc. */ - package org.netbeans.modules.dlight.terminal.action; import java.awt.Component; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; +import java.net.ConnectException; import javax.swing.Action; import javax.swing.Icon; import javax.swing.JButton; -import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.HostInfo; -import org.netbeans.modules.nativeexecution.api.NativeProcess; -import org.netbeans.modules.nativeexecution.api.NativeProcessBuilder; -import org.netbeans.modules.nativeexecution.api.execution.NativeExecutionDescriptor; -import org.netbeans.modules.nativeexecution.api.execution.NativeExecutionService; -import org.netbeans.modules.nativeexecution.api.pty.PtySupport; -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager; -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager.CancellationException; -import org.netbeans.modules.nativeexecution.api.util.HostInfoUtils; +import org.netbeans.modules.dlight.api.terminal.ProcessTerminal; +import org.netbeans.modules.dlight.spi.terminal.ShellConfiguration; +import org.netbeans.modules.dlight.spi.terminal.TerminalConfiguration; +import org.netbeans.modules.terminal.api.IOEmulation; import org.netbeans.modules.terminal.api.IONotifier; +import org.netbeans.modules.terminal.api.IOResizable; +import org.netbeans.modules.terminal.api.IOTerm; import org.netbeans.modules.terminal.api.IOVisibility; -import org.openide.DialogDisplayer; -import org.openide.NotifyDescriptor; import org.openide.util.Exceptions; import org.openide.util.ImageUtilities; -import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; -import org.openide.util.WeakListeners; import org.openide.windows.IOContainer; import org.openide.windows.IOProvider; import org.openide.windows.InputOutput; @@ -86,11 +69,13 @@ * @author Vladimir Voskresensky */ public final class TerminalSupportImpl { + private static final RequestProcessor RP = new RequestProcessor("Terminal Action RP", 100); // NOI18N private TerminalSupportImpl() { + super(); } - + public static Component getToolbarPresenter(Action action) { JButton button = new JButton(action); button.setBorderPainted(false); @@ -107,163 +92,83 @@ button.setDisabledIcon(ImageUtilities.createDisabledIcon((Icon) icon)); return button; } - - public static void openTerminalImpl(final IOContainer ioContainer, final String tabTitle, final ExecutionEnvironment env, final String dir, final boolean silentMode) { + + public static void openTerminalImpl(final IOContainer ioContainer, final String title, + final org.netbeans.api.extexecution.ProcessBuilder.Provider provider, final String dir, final boolean silentMode) { + final IOProvider term = IOProvider.get("Terminal"); // NOI18N - if (term != null) { - final AtomicBoolean destroyed = new AtomicBoolean(false); - Runnable runnable = new Runnable() { - @Override - public void run() { - if (SwingUtilities.isEventDispatchThread()) { - ioContainer.requestActive(); - } else { - doWork(); - } - } - - private void doWork() { - if (!ConnectionManager.getInstance().isConnectedTo(env)) { - try { - ConnectionManager.getInstance().connectTo(env); - } catch (IOException ex) { - if (!destroyed.get()) { - String error = ex.getCause() == null ? ex.getMessage() : ex.getCause().getMessage(); - String msg = NbBundle.getMessage(TerminalSupportImpl.class, "TerminalAction.FailedToStart.text", error); // NOI18N - DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE)); - } - return; - } catch (CancellationException ex) { - return; - } - } - - final HostInfo hostInfo; - try { - hostInfo = HostInfoUtils.getHostInfo(env); - boolean isSupported = PtySupport.isSupportedFor(env); - if (!isSupported) { - if (!silentMode) { - String message; - - if (hostInfo.getOSFamily() == HostInfo.OSFamily.WINDOWS) { - message = NbBundle.getMessage(TerminalSupportImpl.class, "LocalTerminalNotSupported.error.nocygwin"); // NOI18N - } else { - message = NbBundle.getMessage(TerminalSupportImpl.class, "LocalTerminalNotSupported.error"); // NOI18N - } - - NotifyDescriptor nd = new NotifyDescriptor.Message(message, NotifyDescriptor.INFORMATION_MESSAGE); - DialogDisplayer.getDefault().notify(nd); - } - return; - } - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - return; - } catch (CancellationException ex) { - Exceptions.printStackTrace(ex); - return; - } - - final AtomicReference ioRef = new AtomicReference(); - try { - ioRef.set(term.getIO(tabTitle, null, ioContainer)); - - NativeProcessBuilder npb = NativeProcessBuilder.newProcessBuilder(env); - npb.addNativeProcessListener(new NativeProcessListener(ioRef.get(), destroyed)); - - String shell = hostInfo.getLoginShell(); - if (dir != null) { - npb.setWorkingDirectory(dir); - } -// npb.setWorkingDirectory("${HOME}"); - npb.setExecutable(shell); - NativeExecutionDescriptor descr; - descr = new NativeExecutionDescriptor().controllable(true).frontWindow(true).inputVisible(true).inputOutput(ioRef.get()); - descr.postExecution(new Runnable() { - - @Override - public void run() { - ioRef.get().closeInputOutput(); - } - }); - NativeExecutionService es = NativeExecutionService.newService(npb, descr, "Terminal Emulator"); // NOI18N - Future result = es.run(); - // ask terminal to become active - SwingUtilities.invokeLater(this); - - try { - // if terminal can not be started then ExecutionException should be thrown - // wait one second to see if terminal can not be started. otherwise it's OK to exit by TimeOut - result.get(1, TimeUnit.SECONDS); - } catch (TimeoutException ex) { - // we should be there - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); - } catch (ExecutionException ex) { - if (!destroyed.get()) { - String error = ex.getCause() == null ? ex.getMessage() : ex.getCause().getMessage(); - String msg = NbBundle.getMessage(TerminalSupportImpl.class, "TerminalAction.FailedToStart.text", error); // NOI18N - DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE)); - } - } - } catch (java.util.concurrent.CancellationException ex) { // VK: don't quite understand who can throw it? - Exceptions.printStackTrace(ex); - reportInIO(ioRef.get(), ex); - } - } - - private void reportInIO(InputOutput io, Exception ex) { - if (io != null && ex != null) { - io.getErr().print(ex.getLocalizedMessage()); - } - } - }; - RP.post(runnable); - } - } - - private final static class NativeProcessListener implements ChangeListener, PropertyChangeListener { - - private final AtomicReference processRef; - private final AtomicBoolean destroyed; - - public NativeProcessListener(InputOutput io, AtomicBoolean destroyed) { - assert destroyed != null; - this.destroyed = destroyed; - this.processRef = new AtomicReference(); - IONotifier.addPropertyChangeListener(io, WeakListeners.propertyChange(NativeProcessListener.this, io)); + if (term == null) { + return; } - @Override - public void stateChanged(ChangeEvent e) { - NativeProcess process = processRef.get(); - if (process == null && e.getSource() instanceof NativeProcess) { - processRef.compareAndSet(null, (NativeProcess) e.getSource()); + try { + org.netbeans.api.extexecution.ProcessBuilder pb; + try { + pb = provider.getProcessBuilder(); + } catch (ConnectException ex) { + return; } - } - @Override - public void propertyChange(PropertyChangeEvent evt) { - if (IOVisibility.PROP_VISIBILITY.equals(evt.getPropertyName()) && Boolean.FALSE.equals(evt.getNewValue())) { - if (destroyed.compareAndSet(false, true)) { - // term is closing => destroy process - final NativeProcess proc = processRef.get(); - if (proc != null) { + final InputOutput termIO = term.getIO(title != null ? title : pb.getDescription(), + null, ioContainer); + + ShellConfiguration shellConfiguration = pb.getLookup().lookup(ShellConfiguration.class); + if (shellConfiguration == null) { + throw new IllegalStateException("No shell provided by the builder: " + pb.getDescription()); + } + pb.setExecutable(shellConfiguration.getShell()); + TerminalConfiguration tcc = pb.getLookup().lookup(TerminalConfiguration.class); + + if (tcc != null) { + tcc.setTerminalMode(true); + tcc.setEmulation(IOEmulation.getEmulation(termIO)); + } + + if (dir != null) { + pb.setWorkingDirectory(dir); + } + + final Process shell = pb.call(); + + IOTerm.connect(termIO, shell.getOutputStream(), shell.getInputStream(), shell.getErrorStream()); + IONotifier.addPropertyChangeListener(termIO, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (IOVisibility.PROP_VISIBILITY.equals(evt.getPropertyName()) && Boolean.FALSE.equals(evt.getNewValue())) { RP.submit(new Runnable() { - @Override public void run() { try { - proc.destroy(); + shell.destroy(); } catch (Throwable th) { } } }); + } else if (IOResizable.PROP_SIZE.equals(evt.getPropertyName())) { + IOResizable.Size size = (IOResizable.Size) evt.getNewValue(); + ProcessTerminal.ttyResized(shell, size.cells, size.pixels); } } - } + }); + + ioContainer.requestActive(); + termIO.select(); + + RP.post(new Runnable() { + @Override + public void run() { + try { + shell.waitFor(); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } finally { + termIO.closeInputOutput(); + } + } + }); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); } } } diff --git a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/ui/RemoteInfoDialog.java b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/ui/RemoteInfoDialog.java --- a/dlight.terminal/src/org/netbeans/modules/dlight/terminal/ui/RemoteInfoDialog.java +++ b/dlight.terminal/src/org/netbeans/modules/dlight/terminal/ui/RemoteInfoDialog.java @@ -49,14 +49,14 @@ import java.awt.Component; import java.awt.Toolkit; +import java.net.URI; +import java.net.URISyntaxException; import java.util.prefs.Preferences; import javax.swing.InputVerifier; import javax.swing.JComponent; import javax.swing.JTextField; import javax.swing.text.JTextComponent; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory; -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager; +import org.openide.util.Exceptions; import org.openide.util.NbPreferences; /** @@ -233,10 +233,10 @@ private javax.swing.JTextField userField; // End of variables declaration//GEN-END:variables - private ExecutionEnvironment last; - public ExecutionEnvironment getExecutionEnvironment() { + private URI last; + public URI getConnectionURI() { if (btnKnownHosts.isSelected()) { - last = (ExecutionEnvironment) cbKnownHosts.getSelectedItem(); + last = (URI) cbKnownHosts.getSelectedItem(); } else { if (userField.getText().isEmpty() || hostField.getText().isEmpty()) { return null; @@ -250,19 +250,22 @@ } catch (NumberFormatException ex) { } } - - last = ExecutionEnvironmentFactory.createNew(userField.getText(), hostField.getText(), port); + try { + last = new URI("ssh://" + userField.getText() + '@' + hostField.getText() + ':' + port); + } catch (URISyntaxException ex) { + Exceptions.printStackTrace(ex); + } } Preferences prefs = NbPreferences.forModule(RemoteInfoDialog.class); - prefs.put(LAST_SELECTED_HOST, ExecutionEnvironmentFactory.toUniqueID(last)); + prefs.put(LAST_SELECTED_HOST, last.toString()); return last; } private void fillHosts() { - cbKnownHosts.removeAllItems(); - for (ExecutionEnvironment ee : ConnectionManager.getInstance().getRecentConnections()) { - cbKnownHosts.addItem(ee); - } + cbKnownHosts.removeAllItems(); +// for (ExecutionEnvironment ee : ConnectionManager.getInstance().getRecentConnections()) { +// cbKnownHosts.addItem(ee); +// } boolean hasKnown = cbKnownHosts.getItemCount() > 0; btnKnownHosts.setEnabled(hasKnown); if (hasKnown) { @@ -289,7 +292,11 @@ Preferences prefs = NbPreferences.forModule(RemoteInfoDialog.class); String eeID = prefs.get(LAST_SELECTED_HOST, ""); if (!eeID.isEmpty()) { - last = ExecutionEnvironmentFactory.fromUniqueID(eeID); + try { + last = new URI(eeID); + } catch (URISyntaxException ex) { + Exceptions.printStackTrace(ex); + } } } fillHosts(); diff --git a/execmerge/build.xml b/execmerge/build.xml new file mode 100644 --- /dev/null +++ b/execmerge/build.xml @@ -0,0 +1,5 @@ + + + Builds, tests, and runs the project org.netbeans.modules.execmerge + + diff --git a/execmerge/manifest.mf b/execmerge/manifest.mf new file mode 100644 --- /dev/null +++ b/execmerge/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.execmerge +OpenIDE-Module-Implementation-Version: 1 +OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/execmerge/resources/Bundle.properties +AutoUpdate-Show-In-Client: false diff --git a/execmerge/nbproject/project.properties b/execmerge/nbproject/project.properties new file mode 100644 --- /dev/null +++ b/execmerge/nbproject/project.properties @@ -0,0 +1,3 @@ +javac.source=1.6 +javac.compilerargs=-Xlint -Xlint:-serial +spec.version.base=1.0 diff --git a/execmerge/nbproject/project.xml b/execmerge/nbproject/project.xml new file mode 100644 --- /dev/null +++ b/execmerge/nbproject/project.xml @@ -0,0 +1,38 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.execmerge + + + org.netbeans.modules.cnd.execution + + + + 1.0 + + + + org.netbeans.modules.dlight.nativeexecution + + + + 1.26 + + + + org.openide.util + + + + 8.28 + + + + + org.netbeans.modules.execmerge + + + + diff --git a/execmerge/src/org/netbeans/modules/execmerge/ConnectionUtils.java b/execmerge/src/org/netbeans/modules/execmerge/ConnectionUtils.java new file mode 100644 --- /dev/null +++ b/execmerge/src/org/netbeans/modules/execmerge/ConnectionUtils.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.execmerge; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; +import org.openide.util.Exceptions; + +/** + * + * @author akrasny + */ +public final class ConnectionUtils { + + private ConnectionUtils() { + } + + public static Connection ExecutionEnvironment2Connection(ExecutionEnvironment env) { + if (env.isLocal()) { + return ConnectionManager.getLocalConnection(); + } + + try { + URI uri = new URI("ssh://" + env.getUser() + "@" + env.getHost()); // NOI18N + return ConnectionManager.connect(uri); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } catch (URISyntaxException ex) { + Exceptions.printStackTrace(ex); + } + + return ConnectionManager.getLocalConnection(); + } + +} diff --git a/execmerge/src/org/netbeans/modules/execmerge/resources/Bundle.properties b/execmerge/src/org/netbeans/modules/execmerge/resources/Bundle.properties new file mode 100644 --- /dev/null +++ b/execmerge/src/org/netbeans/modules/execmerge/resources/Bundle.properties @@ -0,0 +1,1 @@ +OpenIDE-Module-Name=Temp Module for Execution Merge diff --git a/extexecution/apichanges.xml b/extexecution/apichanges.xml --- a/extexecution/apichanges.xml +++ b/extexecution/apichanges.xml @@ -104,6 +104,7 @@ External Execution API External Execution Input API External Execution Input Printing API + External Execution Process API External Execution Startup Arguments API External Execution SPI External Execution Process Destroy SPI @@ -115,7 +116,50 @@ - + + Enhance API and SPI to make it usable as a proxy to native execution + + + + + +

+ There is new SPI for ProcessBuilder named + ProcessBuilderImplementation2 which now implements + Lookup.Provider to be extensible and provides + a separate Environment object represented by + the EnvironmentImplementation in the SPI. + The ProcessBuilder itself allows access to those + new features via getLookup() and + getEnvironment() methods and it now also + implements Lookup.Provider. The utility class + ProcessParameters wraps up the parameters passed + to the SPI for process creation. Instances of + Environment API are created via + EnvironmentFactory support class. +

+

+ There are also new classes extending a Process + functionality named ProcessCharset, + ProcessId and ProcessSignal allowing + client to get the charset, to get ID and to send signal to + a process. This might not be always supported and the support + may be pre-checked. +

+
+ + + + + + + + + + + + +
Advice to throw UserQuestionException diff --git a/extexecution/arch.xml b/extexecution/arch.xml --- a/extexecution/arch.xml +++ b/extexecution/arch.xml @@ -67,6 +67,13 @@ org.openide.windows.OutputWriter. API provides common implementations too.

+ The support API/SPI + + allows enhancement of process with additional data such as ID, character set + and ability to send signals. Client can later easily check the feature is + supported and perform the query or action. +

+

The SPI allows different implementations of process builder. @@ -170,8 +177,17 @@

The creation of the external process is supported by ExternalProcessBuilder + and ProcessBuilder to make things easier.

+

+ The process returned by default or custom builders may support additional + features. To do so the process must be a Lookup.Provider and put + the implementation of the feature to lookup. The possible extensions are + located in org.netbeans.api.extexecution.process + package. The client then use static methods on respective classes in order + to use the extended functionality. +

@@ -232,8 +248,8 @@

In order to do so it will implement - - ProcessBuilderImplementation and pass + + ProcessBuilderImplementation2 and pass ProcessBuilder to its clients. The API instances are created with help of @@ -510,9 +526,8 @@ -->

- No known platform dependencies. In future there could be need for native code - in order to terminate the whole process tree created by execution of external - process. + No known platform dependencies. The platform dependent way of process + termination is in a separate implementation module.

diff --git a/extexecution/nbproject/project.xml b/extexecution/nbproject/project.xml --- a/extexecution/nbproject/project.xml +++ b/extexecution/nbproject/project.xml @@ -88,6 +88,7 @@ org.netbeans.api.extexecution org.netbeans.api.extexecution.print + org.netbeans.api.extexecution.process org.netbeans.api.extexecution.input org.netbeans.api.extexecution.startup org.netbeans.spi.extexecution diff --git a/extexecution/src/org/netbeans/api/extexecution/Environment.java b/extexecution/src/org/netbeans/api/extexecution/Environment.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/api/extexecution/Environment.java @@ -0,0 +1,156 @@ +/* + * 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.api.extexecution; + +import java.util.Map; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.extexecution.EnvironmentAccessor; +import org.netbeans.spi.extexecution.EnvironmentImplementation; +import org.openide.util.Parameters; + +/** + * The class that provides an access to environment variables. + * + * @see ProcessBuilder#getEnvironment() + * @see EnvironmentImplementation + * @author Petr Hejl + * @since 1.37 + */ +public final class Environment { + + private final EnvironmentImplementation implementation; + + static { + EnvironmentAccessor.setDefault(new EnvironmentAccessor() { + + @Override + public Environment createEnvironment(EnvironmentImplementation impl) { + return new Environment(impl); + } + }); + } + + private Environment(EnvironmentImplementation implementation) { + this.implementation = implementation; + } + + /** + * Returns the value of the variable or null. + * + * @param name the name of the variable + * @return the value of the variable or null + */ + @CheckForNull + public String getVariable(@NonNull String name) { + Parameters.notNull("name", name); + + return implementation.getVariable(name); + } + + /** + * Appends a path to a path-like variable. The proper path separator is used + * to separate the new value. + * + * @param name the name of the variable such as for example + * PATH or LD_LIBRARY_PATH + * @param value the value (path to append) + */ + public void appendPath(@NonNull String name, @NonNull String value) { + Parameters.notNull("name", name); + Parameters.notNull("value", value); + + implementation.appendPath(name, value); + } + + /** + * Prepends a path to a path-like variable. The proper path separator is used + * to separate the new value. + * + * @param name the name of the variable such as for example + * PATH or LD_LIBRARY_PATH + * @param value the value (path to prepend) + */ + public void prependPath(@NonNull String name, @NonNull String value) { + Parameters.notNull("name", name); + Parameters.notNull("value", value); + + implementation.prependPath(name, value); + } + + /** + * Sets a value for a variable with the given name. + * + * @param name the name of the variable + * @param value the value + */ + public void setVariable(@NonNull String name, @NonNull String value) { + Parameters.notNull("name", name); + Parameters.notNull("value", value); + + implementation.setVariable(name, value); + } + + /** + * Removes a variable with the given name. The subsequent call to + * {@link #getVariable(java.lang.String)} with the same argument will return + * null. + * + * @param name the name of the variable + */ + public void removeVariable(@NonNull String name) { + Parameters.notNull("name", name); + + implementation.removeVariable(name); + } + + /** + * Returns all variable names and associated values as a {@link Map}. + * Changes to the map are not propagated back to the {@link Environment}. + * + * @return all variable names and associated values + */ + @NonNull + public Map values() { + return implementation.values(); + } +} diff --git a/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java b/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java --- a/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java +++ b/extexecution/src/org/netbeans/api/extexecution/ExecutionService.java @@ -52,6 +52,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; +import java.io.Writer; import java.nio.charset.Charset; import java.security.AccessController; import java.security.PrivilegedAction; @@ -66,10 +67,12 @@ import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; import org.netbeans.modules.extexecution.ProcessInputStream; @@ -79,7 +82,9 @@ import org.netbeans.api.extexecution.input.InputProcessors; import org.netbeans.api.extexecution.input.InputReaderTask; import org.netbeans.api.extexecution.input.InputReaders; +import org.netbeans.api.extexecution.input.LineProcessor; import org.netbeans.api.extexecution.input.LineProcessors; +import org.netbeans.api.extexecution.process.ProcessCharset; import org.openide.util.Cancellable; import org.openide.util.Mutex; import org.openide.util.NbBundle; @@ -91,20 +96,20 @@ * Execution service provides the facility to execute the process while * displaying the output and handling the input. *

- * It will execute the program with an associated I/O window, with stop and - * restart buttons. It will also obey various descriptor properties such as - * whether or not to show a progress bar. + * It will execute the program with an associated I/O window, possibly with + * stop and restart buttons. It will also obey various descriptor properties + * such as whether or not to show a progress bar. *

* All processes launched by this class are terminated on VM exit (if * these are not finished or terminated earlier). *

- * Note that once service is run for the first time, subsequents runs can be + * Note that once service is run for the first time, subsequent runs can be * invoked by the user (rerun button) if it is allowed to do so * ({@link ExecutionDescriptor#isControllable()}). * *

*

- * Sample usage - executing ls command: + * Sample usage (ls command): *

  *     ExecutionDescriptor descriptor = new ExecutionDescriptor()
  *          .frontWindow(true).controllable(true);
@@ -114,6 +119,14 @@
  *     ExecutionService service = ExecutionService.newService(processBuilder, descriptor, "ls command");
  *     Future<Integer> task = service.run();
  * 
+ *

+ * Even simpler usage but without displaying output (ls command): + *

+ *     ExternalProcessBuilder processBuilder = new ExternalProcessBuilder("ls");
+ *
+ *     ExecutionService service = ExecutionService.newService(processBuilder, null, null);
+ *     Future<Integer> task = service.run();
+ * 
*
* * @author Petr Hejl @@ -130,6 +143,21 @@ private static final ExecutorService EXECUTOR_SERVICE = new RequestProcessor(ExecutionService.class.getName(), Integer.MAX_VALUE); + private static final InputProcessor NULL_PROCESSOR = new InputProcessor() { + + @Override + public void processInput(char[] chars) throws IOException { + } + + @Override + public void reset() throws IOException { + } + + @Override + public void close() throws IOException { + } + }; + static { // rerun accessor RerunAction.Accessor.setDefault(new RerunAction.Accessor() { @@ -156,16 +184,25 @@ }); } - private final Callable processCreator; + private final Callable processCreator; private final ExecutionDescriptor descriptor; private final String originalDisplayName; - private ExecutionService(Callable processCreator, String displayName, ExecutionDescriptor descriptor) { + private final Reader processInput; + + private final boolean rerunAllowed; + + private final AtomicInteger runCount = new AtomicInteger(); + + private ExecutionService(Callable processCreator, String displayName, + ExecutionDescriptor descriptor, Reader processInput, boolean rerunAllowed) { this.processCreator = processCreator; this.originalDisplayName = displayName; this.descriptor = descriptor; + this.processInput = processInput; + this.rerunAllowed = rerunAllowed; } /** @@ -178,24 +215,130 @@ * @return new execution service */ @NonNull - public static ExecutionService newService(@NonNull Callable processCreator, + public static ExecutionService newService(@NonNull Callable processCreator, @NonNull ExecutionDescriptor descriptor, @NonNull String displayName) { - return new ExecutionService(processCreator, displayName, descriptor); + return new ExecutionService(processCreator, displayName, descriptor, null, true); + } + + /** + * Creates new execution service. Service will wrap up the processes + * created by processCreator and will manage them. The service + * may not be run more than once otherwise {@link IllegalStateException} + * is thrown on {@link #run()}. No UI will be displayed. + *

+ * Passed processInput will be implicitly closed when the execution (invoked by + * {@link #run()}) is finished. The client should not use the reader passed + * as argument anymore or the reader should be designed thread safe. + * + * @param processCreator callable returning the process to wrap up + * @param outputProcessor processor processing lines of standard output; + * may be null + * @param errorProcessor processor processing lines of standard error + * output; may be null + * @param processorInput reader providing input for standard input of + * the created process. Once passed it either should not be + * used anymore or it should be designed to be thread safe. + * May be null. + * @return new execution service + * @since 1.37 + */ + @NonNull + public static ExecutionService newService(@NonNull Callable processCreator, + @NullAllowed final LineProcessor outputProcessor, @NullAllowed final LineProcessor errorProcessor, + @NullAllowed final Reader processorInput) { + ExecutionDescriptor descriptor = new ExecutionDescriptor() + .inputOutput(InputOutput.NULL); + + if (outputProcessor != null) { + descriptor = descriptor.outProcessorFactory(new InputProcessorFactory() { + @Override + public InputProcessor newInputProcessor(InputProcessor defaultProcessor) { + return InputProcessors.bridge(outputProcessor); + } + }); + } + if (errorProcessor != null) { + descriptor = descriptor.errProcessorFactory(new InputProcessorFactory() { + @Override + public InputProcessor newInputProcessor(InputProcessor defaultProcessor) { + return InputProcessors.bridge(errorProcessor); + } + }); + } + + return new ExecutionService(processCreator, null, descriptor, processorInput, false); + } + + /** + * Creates new execution service. Service will wrap up the processes + * created by processCreator and will manage them. The service + * may not be run more than once otherwise {@link IllegalStateException} + * is thrown on {@link #run()}. No UI will be displayed. + *

+ * Passed inputWriter and errorWriter will be implicitly closed when the + * execution (invoked by {@link #run()}) is finished. The client either + * should not use the passed writers anymore or these should be designed + * to be thread safe. + * + * @param processCreator callable returning the process to wrap up + * @param outputWriter where to write standard output of the process. Once + * passed it either should not be used anymore or it should + * be designed to be thread safe. May be null. + * @param errorWriter where to write standard error output of the process. + * Once passed it either should not be used anymore or it should + * be designed to be thread safe. May be null. + * @return new execution service + * @since 1.37 + */ + public static ExecutionService newService(@NonNull Callable processCreator, + @NullAllowed final Writer outputWriter, @NullAllowed final Writer errorWriter) { + ExecutionDescriptor descriptor = new ExecutionDescriptor() + .inputOutput(InputOutput.NULL); + + if (outputWriter != null) { + descriptor = descriptor.outProcessorFactory(new InputProcessorFactory() { + @Override + public InputProcessor newInputProcessor(InputProcessor defaultProcessor) { + return InputProcessors.copying(outputWriter); + } + }); + } + if (errorWriter != null) { + descriptor = descriptor.errProcessorFactory(new InputProcessorFactory() { + @Override + public InputProcessor newInputProcessor(InputProcessor defaultProcessor) { + return InputProcessors.copying(errorWriter); + } + }); + } + + return new ExecutionService(processCreator, null, descriptor, null, false); } /** * Runs the process described by this service. The call does not block * and the task is represented by the returned value. Integer returned * as a result of the {@link Future} is exit code of the process. + * The ability to call this method multiple times depends on a way + * in which the service has been created. Only service created by + * {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.ExecutionDescriptor, java.lang.String)} + * may be run more than once. *

* The output tabs are reused (if caller does not use the custom one, - * see {@link ExecutionDescriptor#getInputOutput()}) - the tab to reuse - * (if any) is selected by having the same name and same buttons - * (control and option). If there is no output tab to reuse new one - * is opened. + * see {@link ExecutionDescriptor#getInputOutput()} or if it is not being + * run without output tab, see {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.input.LineProcessor, org.netbeans.api.extexecution.input.LineProcessor, java.io.Reader)}) + * - the tab to reuse (if any) is selected by having the same name + * and same buttons (control and option). If there is no output tab to + * reuse new one is opened. *

- * This method can be invoked multiple times returning the different and - * unrelated {@link Future}s. On each call Callable<Process> + * If the service has been created by {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.input.LineProcessor, org.netbeans.api.extexecution.input.LineProcessor, java.io.Reader)} + * this method may be called only once. Subsequent calls will cause an + * {@link IllegalStateException} to be thrown. + * If the service has been created by {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.ExecutionDescriptor, java.lang.String)} + * this method can be invoked multiple times returning the different and + * unrelated {@link Future}s. + *

+ * On each call Callable<Process> * passed to {@link #newService(java.util.concurrent.Callable, org.netbeans.api.extexecution.ExecutionDescriptor, java.lang.String)} * is invoked in order to create the process. If the process creation fails * (throwing an exception) returned Future will throw @@ -213,6 +356,10 @@ } private Future run(InputOutput required) { + if (!rerunAllowed && runCount.incrementAndGet() > 1) { + throw new IllegalStateException("Run invoked multimple times"); + } + final InputOutputManager.InputOutputData ioData = getInputOutput(required); final String displayName = ioData.getDisplayName(); @@ -220,9 +367,16 @@ final ProgressHandle handle = createProgressHandle(ioData.getInputOutput(), displayName, cancellable); final InputOutput io = ioData.getInputOutput(); - final OutputWriter out = io.getOut(); - final OutputWriter err = io.getErr(); - final Reader in = io.getIn(); + assert processInput == null || io == InputOutput.NULL; + + final Reader in; + if (processInput != null) { + in = processInput; + } else if (descriptor.isInputVisible() && io != InputOutput.NULL) { + in = io.getIn(); + } else { + in = null; + } final CountDownLatch finishedLatch = new CountDownLatch(1); @@ -267,23 +421,37 @@ outStream = new ProcessInputStream(process, process.getInputStream()); errStream = new ProcessInputStream(process, process.getErrorStream()); - executor = Executors.newFixedThreadPool(descriptor.isInputVisible() ? 3 : 2); + executor = Executors.newFixedThreadPool(in != null ? 3 : 2); Charset charset = descriptor.getCharset(); + Charset inputCharset = charset; + Charset outputCharset = charset; + Charset errorCharset = charset; if (charset == null) { - charset = Charset.defaultCharset(); + inputCharset = ProcessCharset.getInputCharset(process); + outputCharset = ProcessCharset.getOutputCharset(process); + errorCharset = ProcessCharset.getErrorCharset(process); + } + if (inputCharset == null) { + inputCharset = Charset.defaultCharset(); + } + if (outputCharset == null) { + outputCharset = Charset.defaultCharset(); + } + if (errorCharset == null) { + errorCharset = Charset.defaultCharset(); } tasks.add(InputReaderTask.newDrainingTask( - InputReaders.forStream(new BufferedInputStream(outStream), charset), - createOutProcessor(out))); + InputReaders.forStream(new BufferedInputStream(outStream), outputCharset), + createOutProcessor(io))); tasks.add(InputReaderTask.newDrainingTask( - InputReaders.forStream(new BufferedInputStream(errStream), charset), - createErrProcessor(err))); - if (descriptor.isInputVisible()) { + InputReaders.forStream(new BufferedInputStream(errStream), errorCharset), + createErrProcessor(io))); + if (in != null) { tasks.add(InputReaderTask.newTask( InputReaders.forReader(in), - createInProcessor(process.getOutputStream()))); + createInProcessor(process.getOutputStream(), inputCharset))); } for (InputReaderTask task : tasks) { executor.submit(task); @@ -568,15 +736,20 @@ Mutex.EVENT.readAccess(ui); } - private InputProcessor createOutProcessor(OutputWriter writer) { - LineConvertorFactory convertorFactory = descriptor.getOutConvertorFactory(); + private InputProcessor createOutProcessor(InputOutput io) { InputProcessor outProcessor = null; - if (descriptor.isOutLineBased()) { - outProcessor = InputProcessors.bridge(LineProcessors.printing(writer, - convertorFactory != null ? convertorFactory.newLineConvertor() : null, true)); + if (io != InputOutput.NULL) { + LineConvertorFactory convertorFactory = descriptor.getOutConvertorFactory(); + OutputWriter writer = io.getOut(); + if (descriptor.isOutLineBased()) { + outProcessor = InputProcessors.bridge(LineProcessors.printing(writer, + convertorFactory != null ? convertorFactory.newLineConvertor() : null, true)); + } else { + outProcessor = InputProcessors.printing(writer, + convertorFactory != null ? convertorFactory.newLineConvertor() : null, true); + } } else { - outProcessor = InputProcessors.printing(writer, - convertorFactory != null ? convertorFactory.newLineConvertor() : null, true); + outProcessor = NULL_PROCESSOR; } InputProcessorFactory descriptorOutFactory = descriptor.getOutProcessorFactory(); @@ -587,15 +760,20 @@ return outProcessor; } - private InputProcessor createErrProcessor(OutputWriter writer) { - LineConvertorFactory convertorFactory = descriptor.getErrConvertorFactory(); + private InputProcessor createErrProcessor(InputOutput io) { InputProcessor errProcessor = null; - if (descriptor.isErrLineBased()) { - errProcessor = InputProcessors.bridge(LineProcessors.printing(writer, - convertorFactory != null ? convertorFactory.newLineConvertor() : null, false)); + if (io != InputOutput.NULL) { + LineConvertorFactory convertorFactory = descriptor.getErrConvertorFactory(); + OutputWriter writer = io.getErr(); + if (descriptor.isErrLineBased()) { + errProcessor = InputProcessors.bridge(LineProcessors.printing(writer, + convertorFactory != null ? convertorFactory.newLineConvertor() : null, false)); + } else { + errProcessor = InputProcessors.printing(writer, + convertorFactory != null ? convertorFactory.newLineConvertor() : null, false); + } } else { - errProcessor = InputProcessors.printing(writer, - convertorFactory != null ? convertorFactory.newLineConvertor() : null, false); + errProcessor = NULL_PROCESSOR; } InputProcessorFactory descriptorErrFactory = descriptor.getErrProcessorFactory(); @@ -606,8 +784,8 @@ return errProcessor; } - private InputProcessor createInProcessor(OutputStream os) { - return InputProcessors.copying(new OutputStreamWriter(os)); + private InputProcessor createInProcessor(OutputStream os, Charset charset) { + return InputProcessors.copying(new OutputStreamWriter(os, charset)); } private static class ProgressCancellable implements Cancellable { diff --git a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java --- a/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java +++ b/extexecution/src/org/netbeans/api/extexecution/ExternalProcessBuilder.java @@ -100,6 +100,8 @@ private final Map envVariables = new HashMap(); + private final boolean emptySystemVariables; + /** * Creates the new builder that will create the process by running * given executable. Arguments must not be part of the string. @@ -117,6 +119,7 @@ this.arguments.addAll(builder.arguments); this.paths.addAll(builder.paths); this.envVariables.putAll(builder.envVariables); + this.emptySystemVariables = builder.emptySystemVariables; } /** @@ -286,7 +289,13 @@ } Map pbEnv = pb.environment(); - Map env = buildEnvironment(pbEnv); + Map env; + if (emptySystemVariables) { + pbEnv.clear(); + env = new HashMap(); + } else { + env = buildEnvironment(pbEnv); + } pbEnv.putAll(env); String uuid = UUID.randomUUID().toString(); pbEnv.put(WrapperProcess.KEY_UUID, uuid); @@ -297,6 +306,11 @@ return wp; } + ExternalProcessBuilder emptySystemVariables(boolean emptySystemVariables) { + BuilderData builder = new BuilderData(this); + return new ExternalProcessBuilder(builder.emptySystemVariables(emptySystemVariables)); + } + /** * Logs the given pb using the given level. * @@ -331,18 +345,7 @@ // Find PATH environment variable - on Windows it can be some other // case and we should use whatever it has. - String pathName = "PATH"; // NOI18N - - if (Utilities.isWindows()) { - pathName = "Path"; // NOI18N - - for (String key : ret.keySet()) { - if ("PATH".equals(key.toUpperCase(Locale.ENGLISH))) { // NOI18N - pathName = key; - break; - } - } - } + String pathName = getPathName(original); // TODO use StringBuilder String currentPath = ret.get(pathName); @@ -362,6 +365,7 @@ return ret; } + // package level for unit testing List buildArguments() { if (!Utilities.isWindows()) { @@ -378,6 +382,44 @@ return result; } + static void putPath(File path, String pathName, boolean prepend, Map current) { + String currentPath = current.get(pathName); + + if (currentPath == null) { + currentPath = ""; + } + + if (prepend) { + currentPath = path.getAbsolutePath().replace(" ", "\\ ") //NOI18N + + File.pathSeparator + currentPath; + } else { + currentPath = currentPath + File.pathSeparator + + path.getAbsolutePath().replace(" ", "\\ "); //NOI18N + } + + if (!"".equals(currentPath.trim())) { + current.put(pathName, currentPath); + } + } + + static String getPathName(Map systemEnv) { + // Find PATH environment variable - on Windows it can be some other + // case and we should use whatever it has. + String pathName = "PATH"; // NOI18N + + if (Utilities.isWindows()) { + pathName = "Path"; // NOI18N + + for (String keySystem : systemEnv.keySet()) { + if ("PATH".equals(keySystem.toUpperCase(Locale.ENGLISH))) { // NOI18N + pathName = keySystem; + break; + } + } + } + return pathName; + } + private static String escapeString(String s) { if (s.length() == 0) { return "\"\""; // NOI18N @@ -475,6 +517,8 @@ private Map envVariables = new HashMap(); + private boolean emptySystemVariables; + public BuilderData(String executable) { this.executable = executable; } @@ -486,6 +530,7 @@ this.arguments.addAll(builder.arguments); this.paths.addAll(builder.paths); this.envVariables.putAll(builder.envVariables); + this.emptySystemVariables = builder.emptySystemVariables; } public BuilderData workingDirectory(File workingDirectory) { @@ -521,6 +566,11 @@ envVariables.put(name, value); return this; } + + public BuilderData emptySystemVariables(boolean emptySystemVariables) { + this.emptySystemVariables = emptySystemVariables; + return this; + } } diff --git a/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java b/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java --- a/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java +++ b/extexecution/src/org/netbeans/api/extexecution/ProcessBuilder.java @@ -46,12 +46,19 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.Callable; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.modules.extexecution.ProcessBuilderAccessor; +import org.netbeans.modules.extexecution.ProcessParametersAccessor; +import org.netbeans.spi.extexecution.EnvironmentFactory; +import org.netbeans.spi.extexecution.EnvironmentImplementation; import org.netbeans.spi.extexecution.ProcessBuilderImplementation; +import org.netbeans.spi.extexecution.ProcessBuilderImplementation2; +import org.netbeans.spi.extexecution.ProcessParameters; +import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.Parameters; import org.openide.util.UserQuestionException; @@ -68,35 +75,49 @@ * for creating the local machine OS processes. *

* Thread safety of this class depends on thread safety of - * the {@link ProcessBuilderImplementation} the class is using. If it is thread + * the implementation class. + *

+ * If the {@link ProcessBuilderImplementation} is used and it is thread * safe (if possible the implementation should be even stateless) this class * is thread safe as well. + *

+ * If the {@link ProcessBuilderImplementation2} is used and it is (including + * {@link EnvironmentImplementation}) thread safe and does not have any mutable + * configuration accessible via {@link ProcessBuilderImplementation2#getLookup()} + * it is thread safe as well. Otherwise it is not thread safe. + *

+ * The synchronization mechanism use in this object is the {@link ProcessBuilderImplementation} + * or {@link ProcessBuilderImplementation2} object monitor. * * @author Petr Hejl * @since 1.28 */ -public final class ProcessBuilder implements Callable { +public final class ProcessBuilder implements Callable, Lookup.Provider { private final ProcessBuilderImplementation implementation; + private final ProcessBuilderImplementation2 implementation2; + + private final Object lock; + private final String description; - /**GuardedBy("this")*/ + /**GuardedBy("lock")*/ private String executable; - /**GuardedBy("this")*/ + /**GuardedBy("lock")*/ private String workingDirectory; - /**GuardedBy("this")*/ + /**GuardedBy("lock")*/ private List arguments = new ArrayList(); - /**GuardedBy("this")*/ + /**GuardedBy("lock")*/ private List paths = new ArrayList(); - /**GuardedBy("this")*/ + /**GuardedBy("lock")*/ private Map envVariables = new HashMap(); - /**GuardedBy("this")*/ + /**GuardedBy("lock")*/ private boolean redirectErrorStream; static { @@ -106,12 +127,32 @@ public ProcessBuilder createProcessBuilder(ProcessBuilderImplementation impl, String description) { return new ProcessBuilder(impl, description); } + + @Override + public ProcessBuilder createProcessBuilder(ProcessBuilderImplementation2 impl, String description) { + return new ProcessBuilder(impl, description); + } }); } private ProcessBuilder(ProcessBuilderImplementation implementation, String description) { + this(implementation, null, description); + } + + private ProcessBuilder(ProcessBuilderImplementation2 implementation2, String description) { + this(null, implementation2, description); + } + + private ProcessBuilder(ProcessBuilderImplementation implementation, ProcessBuilderImplementation2 implementation2, String description) { + assert implementation == null || implementation2 == null; this.implementation = implementation; + this.implementation2 = implementation2; this.description = description; + if (implementation != null) { + lock = implementation; + } else { + lock = implementation2; + } } /** @@ -122,7 +163,7 @@ * machine */ public static ProcessBuilder getLocal() { - return new ProcessBuilder(new LocalProcessFactory(), + return new ProcessBuilder(new LocalProcessBuilder(), NbBundle.getMessage(ProcessBuilder.class, "LocalProcessBuilder")); } @@ -146,7 +187,7 @@ public void setExecutable(@NonNull String executable) { Parameters.notNull("executable", executable); - synchronized (this) { + synchronized (lock) { this.executable = executable; } } @@ -158,7 +199,7 @@ * @param workingDirectory the working directory of the process */ public void setWorkingDirectory(@NullAllowed String workingDirectory) { - synchronized (this) { + synchronized (lock) { this.workingDirectory = workingDirectory; } } @@ -172,7 +213,7 @@ public void setArguments(@NonNull List arguments) { Parameters.notNull("arguments", arguments); - synchronized (this) { + synchronized (lock) { this.arguments.clear(); this.arguments.addAll(arguments); } @@ -184,11 +225,20 @@ * exception of PATH possibly configured by {@link #setPaths(java.util.List)}. * * @param envVariables the environment variables for the process + * @deprecated use {@link Environment#setVariable(java.lang.String, java.lang.String)} + * on object received by {@link #getEnvironment()} call */ public void setEnvironmentVariables(@NonNull Map envVariables) { Parameters.notNull("envVariables", envVariables); - synchronized (this) { + if (implementation2 != null) { + for (Map.Entry entry : envVariables.entrySet()) { + implementation2.getEnvironment().setVariable(entry.getKey(), entry.getValue()); + } + return; + } + + synchronized (lock) { this.envVariables.clear(); this.envVariables.putAll(envVariables); } @@ -196,15 +246,32 @@ /** * Sets the additional paths to be included in PATH environment - * variable for the process. + * variable for the process. The paths are prepended to the PATH variable + * one by one as specified in the list so in fact the last path in + * the list will be the first one in the PATH variable. + *

+ * This method is semi-deprecated. If {@link #getEnvironment()} is not + * null this call adds paths one by one by call to + * {@link Environment#prependPath(java.lang.String, java.lang.String)} + * on returned object. * * @param paths the additional paths to be included in PATH * environment variable + * @deprecated use {@link Environment#prependPath(java.lang.String, java.lang.String)} + * on object received by {@link #getEnvironment()} call, with + * the first argument being PATH */ public void setPaths(@NonNull List paths) { Parameters.notNull("paths", paths); - synchronized (this) { + if (implementation2 != null) { + for (String path : paths) { + implementation2.getEnvironment().prependPath("PATH", path); // NOI18N + } + return; + } + + synchronized (lock) { this.paths.clear(); this.paths.addAll(paths); } @@ -218,12 +285,86 @@ * @param redirectErrorStream the error stream redirection */ public void setRedirectErrorStream(boolean redirectErrorStream) { - synchronized (this) { + synchronized (lock) { this.redirectErrorStream = redirectErrorStream; } } /** + * Returns the object for environment variables manipulation. + * + * @return the object for environment variables manipulation + * @since 1.37 + */ + @NonNull + public Environment getEnvironment() { + if (implementation2 != null) { + return implementation2.getEnvironment(); + } + return EnvironmentFactory.createEnvironment(new EnvironmentImplementation() { + + @Override + public void prependPath(String name, String value) { + if (!name.equalsIgnoreCase("PATH")) { // NOI18N + throw new UnsupportedOperationException("The deprecated implementation " + + implementation.getClass().getName() + "does not support this method for a non PATH variable."); + } + synchronized (lock) { + paths.add(0, value); + } + } + + @Override + public void setVariable(String name, String value) { + synchronized (lock) { + envVariables.put(name, value); + } + } + + @Override + public String getVariable(String name) { + throw new UnsupportedOperationException("The deprecated implementation " + + implementation.getClass().getName() + "does not support this method."); + } + + @Override + public void appendPath(String name, String value) { + throw new UnsupportedOperationException("The deprecated implementation " + + implementation.getClass().getName() + "does not support this method."); + } + + @Override + public void removeVariable(String name) { + throw new UnsupportedOperationException("The deprecated implementation " + + implementation.getClass().getName() + "does not support this method."); + } + + @Override + public Map values() { + throw new UnsupportedOperationException("The deprecated implementation " + + implementation.getClass().getName() + "does not support this method."); + } + }); + } + + /** + * Returns the associated {@link Lookup}. Extension point provided by + * {@link ProcessBuilderImplementation2}. + * + * @return the associated {@link Lookup}. + * @see ProcessBuilderImplementation2#getLookup() + * @since 1.37 + */ + @Override + public Lookup getLookup() { + if (implementation2 != null) { + return implementation2.getLookup(); + } + return Lookup.EMPTY; + } + + + /** * Creates the new {@link Process} based on the properties configured * in this builder. *

@@ -232,7 +373,7 @@ *

* Since version 1.35 implementors of this method are advised to throw * a {@link UserQuestionException} in case the execution cannot be - * performed and requires additional user confirmation, or configuration. + * performed and requires additional user confirmation, or configuration. * Callers of this method may check for this exception and handle it * appropriately. * @@ -248,52 +389,168 @@ @NonNull @Override public Process call() throws IOException { - String currentExecutable = null; - String currentWorkingDirectory = null; + String currentExecutable; + String currentWorkingDirectory; List currentArguments = new ArrayList(); List currentPaths = new ArrayList(); - Map currentEnvVariables = new HashMap(); - boolean currentRedirectErrorStream = false; + Map currentVariables = new HashMap(); + boolean currentRedirectErrorStream; - synchronized (this) { + synchronized (lock) { currentExecutable = executable; currentWorkingDirectory = workingDirectory; currentArguments.addAll(arguments); currentPaths.addAll(paths); - currentEnvVariables.putAll(envVariables); currentRedirectErrorStream = redirectErrorStream; + if (implementation2 != null) { + currentVariables.putAll(getEnvironment().values()); + } else { + currentVariables.putAll(envVariables); + } } if (currentExecutable == null) { throw new IllegalStateException("The executable has not been configured"); } + if (implementation2 != null) { + ProcessParameters params = ProcessParametersAccessor.getDefault().createProcessParameters( + currentExecutable, currentWorkingDirectory, currentArguments, + currentRedirectErrorStream, currentVariables); + return implementation2.createProcess(params); + } + return implementation.createProcess(currentExecutable, currentWorkingDirectory, currentArguments, - currentPaths, currentEnvVariables, currentRedirectErrorStream); + currentPaths, currentVariables, currentRedirectErrorStream); } - private static class LocalProcessFactory implements ProcessBuilderImplementation { + /** + * Marks an object from which it is possible to get a {@link ProcessBuilder}. + * + * @since 1.37 + */ + public static interface Provider { + + /** + * Returns the {@link ProcessBuilder} for the object. + * + * @return the {@link ProcessBuilder} for the object + * @throws IOException if there was a problem with the provision + */ + ProcessBuilder getProcessBuilder() throws IOException; + + } + + private static class LocalProcessBuilder implements ProcessBuilderImplementation2 { + + private final Environment environment = EnvironmentFactory.createEnvironment( + new LocalEnvironment(this, System.getenv())); @Override - public Process createProcess(String executable, String workingDirectory, List arguments, - List paths, Map environment, boolean redirectErrorStream) throws IOException { + public Environment getEnvironment() { + return environment; + } - ExternalProcessBuilder builder = new ExternalProcessBuilder(executable); - if (workingDirectory != null) { - builder = builder.workingDirectory(new File(workingDirectory)); + @Override + public Lookup getLookup() { + return Lookup.EMPTY; + } + + @Override + public Process createProcess(ProcessParameters parameters) throws IOException { + ExternalProcessBuilder builder = new ExternalProcessBuilder(parameters.getExecutable()); + String workingDir = parameters.getWorkingDirectory(); + if (workingDir != null) { + builder = builder.workingDirectory(new File(workingDir)); } - for (String argument : arguments) { + for (String argument : parameters.getArguments()) { builder = builder.addArgument(argument); } - for (String path : paths) { - builder = builder.prependPath(new File(path)); - } - for (Map.Entry entry : environment.entrySet()) { + builder = builder.redirectErrorStream(parameters.isRedirectErrorStream()); + + builder = builder.emptySystemVariables(true); + for (Map.Entry entry : parameters.getEnvironmentVariables().entrySet()) { builder = builder.addEnvironmentVariable(entry.getKey(), entry.getValue()); } - builder = builder.redirectErrorStream(redirectErrorStream); return builder.call(); } } + + private static class LocalEnvironment implements EnvironmentImplementation { + + private final LocalProcessBuilder builder; + + private final Map systemEnvironment; + + private final String pathName; + + public LocalEnvironment(LocalProcessBuilder builder, Map systemEnvironment) { + this.builder = builder; + this.systemEnvironment = new HashMap(systemEnvironment); + this.pathName = ExternalProcessBuilder.getPathName(systemEnvironment); + } + + @Override + public String getVariable(String name) { + synchronized (builder) { + if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N + return systemEnvironment.get(pathName); + } else { + return systemEnvironment.get(name); + } + } + } + + @Override + public void appendPath(String name, String value) { + putPath(name, value, false); + } + + @Override + public void prependPath(String name, String value) { + putPath(name, value, true); + } + + @Override + public void setVariable(String name, String value) { + synchronized (builder) { + if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N + systemEnvironment.put(pathName, value); + } else { + systemEnvironment.put(name, value); + } + } + } + + @Override + public void removeVariable(String name) { + synchronized (builder) { + if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N + systemEnvironment.remove(pathName); + } else { + systemEnvironment.remove(name); + } + } + } + + @Override + public Map values() { + synchronized (builder) { + return new HashMap(systemEnvironment); + } + } + + private void putPath(String name, String value, boolean prepend) { + synchronized (builder) { + if ("PATH".equals(name.toUpperCase(Locale.ENGLISH))) { // NOI18N + ExternalProcessBuilder.putPath(new File(value), pathName, + prepend, systemEnvironment); + } else { + ExternalProcessBuilder.putPath(new File(value), name, + prepend, systemEnvironment); + } + } + } + } } diff --git a/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java b/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java --- a/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java +++ b/extexecution/src/org/netbeans/api/extexecution/input/InputReaders.java @@ -46,6 +46,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.io.StringReader; import java.nio.charset.Charset; import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.annotations.common.NonNull; @@ -82,6 +83,12 @@ */ @NonNull public static InputReader forReader(@NonNull Reader reader) { + if (reader instanceof StringReader) { + // unfortunatelly the string reader is always + // ready (isReady() returns true) which I would consider a bug + // when end of string is reached + return new DefaultInputReader(reader, false); + } return new DefaultInputReader(reader, true); } diff --git a/extexecution/src/org/netbeans/api/extexecution/process/ProcessCharset.java b/extexecution/src/org/netbeans/api/extexecution/process/ProcessCharset.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/api/extexecution/process/ProcessCharset.java @@ -0,0 +1,169 @@ +/* + * 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.api.extexecution.process; + +import java.nio.charset.Charset; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.openide.util.Lookup; +import org.openide.util.Parameters; + +/** + * This class allows client to get input and output charset of + * the {@link Process}. Though the process charsets might not be always + * supported. + *

+ * If the {@link Process} implementation wants to support charsets it has to + * implement {@link Lookup.Provider} and there has to be instance of this class + * in the returned {@link Lookup}. The functionality itself is implemented in + * methods {@link #getInputCharset()} and {@link #getOutputCharset()}. + * + * @author Petr Hejl + * @since 1.37 + */ +public abstract class ProcessCharset { + + private static ProcessCharset find(Process process) { + if (process instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) process; + return p.getLookup().lookup(ProcessCharset.class); + } + return null; + } + + /** + * Checks whether the getting of charsets of the {@link Process} is supported. + * If the return value is false methods + * {@link #getInputCharset(java.lang.Process)} and {@link #getOutputCharset(java.lang.Process)} + * will return null for sure. + * + * @param process the process to check + * @return true if process charsets can be received false + * otherwise + */ + public static boolean isSupported(@NonNull Process process) { + Parameters.notNull("process", process); + + return find(process) != null; + } + + /** + * Returns the input charset of the process if supported. In case it is not + * supported or the process can't determine the charset null is + * returned. + * + * @param process the process + * @return the input charset of the process or null + */ + @CheckForNull + public static Charset getInputCharset(@NonNull Process process) { + Parameters.notNull("process", process); + + ProcessCharset processCharset = find(process); + if (processCharset != null) { + return processCharset.getInputCharset(); + } + return null; + } + + /** + * Returns the output charset of the process if supported. In case it is not + * supported or the process can't determine the charset null is + * returned. + * + * @param process the process + * @return the output charset of the process or null + */ + @CheckForNull + public static Charset getOutputCharset(@NonNull Process process) { + Parameters.notNull("process", process); + + ProcessCharset processCharset = find(process); + if (processCharset != null) { + return processCharset.getOutputCharset(); + } + return null; + } + + /** + * Returns the error output charset of the process if supported. In case it + * is not supported or the process can't determine the charset null + * is returned. + * + * @param process the process + * @return the error output charset of the process or null + */ + @CheckForNull + public static Charset getErrorCharset(@NonNull Process process) { + Parameters.notNull("process", process); + + ProcessCharset processCharset = find(process); + if (processCharset != null) { + return processCharset.getErrorCharset(); + } + return null; + } + + /** + * Returns the input charset of a process or null. + * + * @return input charset of a process or null + */ + @CheckForNull + protected abstract Charset getInputCharset(); + + /** + * Returns the output charset of a process or null. + * + * @return output charset of a process or null + */ + @CheckForNull + protected abstract Charset getOutputCharset(); + + /** + * Returns the error output charset of a process or null. + * + * @return error output charset of a process or null + */ + @CheckForNull + protected abstract Charset getErrorCharset(); +} diff --git a/extexecution/src/org/netbeans/api/extexecution/process/ProcessId.java b/extexecution/src/org/netbeans/api/extexecution/process/ProcessId.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/api/extexecution/process/ProcessId.java @@ -0,0 +1,111 @@ +/* + * 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.api.extexecution.process; + +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.openide.util.Lookup; +import org.openide.util.Parameters; + +/** + * This class allows client to request ID of the {@link Process}. Though + * the process ID might not be always supported. + *

+ * If the {@link Process} implementation wants to support the ID it has to + * implement {@link Lookup.Provider} and there has to be instance of this class + * in the returned {@link Lookup}. The functionality itself is implemented in + * method {@link #getId()}. + * + * @author Petr Hejl + * @since 1.37 + */ +public abstract class ProcessId { + + private static ProcessId find(Process process) { + if (process instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) process; + return p.getLookup().lookup(ProcessId.class); + } + return null; + } + + /** + * Checks whether the getting of the ID of the {@link Process} is supported. + * If the return value is false the {@link #getId(java.lang.Process)} + * will return null for sure. + * + * @param process the process to check + * @return true if the ID can be received false + * otherwise + */ + public static boolean isSupported(@NonNull Process process) { + Parameters.notNull("process", process); + + return find(process) != null; + } + + /** + * Returns the ID of the process if supported. In case it is not supported + * or the process can't determine its ID null is returned. + * + * @param process the process + * @return the process ID or null + */ + @CheckForNull + public static Integer getId(@NonNull Process process) { + Parameters.notNull("process", process); + + ProcessId processId = find(process); + if (processId != null) { + return processId.getId(); + } + return null; + } + + /** + * Returns the ID of a process or null. + * + * @return ID of a process or null + */ + @CheckForNull + protected abstract Integer getId(); +} diff --git a/extexecution/src/org/netbeans/api/extexecution/process/ProcessSignal.java b/extexecution/src/org/netbeans/api/extexecution/process/ProcessSignal.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/api/extexecution/process/ProcessSignal.java @@ -0,0 +1,181 @@ +/* + * 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.api.extexecution.process; + +import java.io.IOException; +import org.netbeans.api.annotations.common.NonNull; +import org.openide.util.Lookup; +import org.openide.util.Parameters; + +/** + * This class allows client send a signal to the {@link Process} or to whole + * its group. Though signal sending might not be always supported. + *

+ * If the {@link Process} implementation wants to support the signal sending + * it has to implement {@link Lookup.Provider} and there has to be instance + * of this class in the returned {@link Lookup}. The functionality itself is + * implemented in methods {@link #signal(org.netbeans.api.extexecution.process.ProcessSignal.Signal)} + * and {@link #signalGroup(org.netbeans.api.extexecution.process.ProcessSignal.Signal)}. + * + * @author Petr Hejl + * @since 1.37 + */ +public abstract class ProcessSignal { + + private static ProcessSignal find(Process process) { + if (process instanceof Lookup.Provider) { + Lookup.Provider p = (Lookup.Provider) process; + return p.getLookup().lookup(ProcessSignal.class); + } + return null; + } + + /** + * Checks whether the signal sending is supported for the {@link Process}. + * If the return value is false the signal operations are + * effectively noops. + * + * @param process the process to check + * @return true if the signals can be send false + * otherwise + */ + public static boolean isSupported(@NonNull Process process) { + Parameters.notNull("process", process); + + return find(process) != null; + } + + /** + * Sends a signal to the process. If the signal sending is not supported + * this is effectively noop. + * + * @param process the process where to send signal + * @param signal the signal to send + * @throws IOException if problem with signal sending occurs + */ + public static void signal(@NonNull Process process, @NonNull Signal signal) throws IOException { + Parameters.notNull("process", process); + Parameters.notNull("signal", signal); + + ProcessSignal processSignal = find(process); + if (processSignal != null) { + processSignal.signal(signal); + } + } + + /** + * Sends a signal to the group the process belongs to. If the signal sending + * is not supported this is effectively noop. + * + * @param process the process marking the group where to send signal + * @param signal the signal to send + * @throws IOException if problem with signal sending occurs + */ + public static void signalGroup(@NonNull Process process, @NonNull Signal signal) throws IOException { + Parameters.notNull("process", process); + Parameters.notNull("signal", signal); + + ProcessSignal processSignal = find(process); + if (processSignal != null) { + processSignal.signalGroup(signal); + } + } + + /** + * Sends a given signal to the process. + * + * @param signal the signal to send + * @throws IOException if problem with signal sending occurs + */ + protected abstract void signal(@NonNull Signal signal) throws IOException; + + /** + * Sends a given signal to the group the process belongs to. + * + * @param signal the signal to send + * @throws IOException if problem with signal sending occurs + */ + protected abstract void signalGroup(@NonNull Signal signal) throws IOException; + + public enum Signal { + + NULL, + SIGHUP, + SIGINT, + SIGQUIT, + SIGILL, + SIGTRAP, + SIGABRT, + SIGEMT, + SIGFPE, + SIGKILL, + SIGBUS, + SIGSEGV, + SIGSYS, + SIGPIPE, + SIGALRM, + SIGTERM, + SIGUSR1, + SIGUSR2, + SIGCHLD, + SIGPWR, + SIGWINCH, + SIGURG, + SIGPOLL, + SIGSTOP, + SIGTSTP, + SIGCONT, + SIGTTIN, + SIGTTOU, + SIGVTALRM, + SIGPROF, + SIGXCPU, + SIGWAITING, + SIGLWP, + SIGFREEZE, + SIGTHAW, + SIGCANCEL, + SIGLOST, + SIGXRES, + SIGJVM1; + } +} diff --git a/extexecution/src/org/netbeans/api/extexecution/process/package-info.java b/extexecution/src/org/netbeans/api/extexecution/process/package-info.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/api/extexecution/process/package-info.java @@ -0,0 +1,50 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-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]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + * + * 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. + */ + +/** + * The API supporting enhanced functionality for + * a {@link java.lang.Process}. + */ +package org.netbeans.api.extexecution.process; + diff --git a/extexecution/src/org/netbeans/modules/extexecution/EnvironmentAccessor.java b/extexecution/src/org/netbeans/modules/extexecution/EnvironmentAccessor.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/modules/extexecution/EnvironmentAccessor.java @@ -0,0 +1,81 @@ +/* + * 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.extexecution; + +import org.netbeans.api.extexecution.Environment; +import org.netbeans.spi.extexecution.EnvironmentImplementation; + +/** + * + * @author Petr Hejl + */ +public abstract class EnvironmentAccessor { + + private static volatile EnvironmentAccessor DEFAULT; + + public static EnvironmentAccessor getDefault() { + EnvironmentAccessor a = DEFAULT; + if (a != null) { + return a; + } + + // invokes static initializer of Environment.class + // that will assign value to the DEFAULT field above + Class c = Environment.class; + try { + Class.forName(c.getName(), true, c.getClassLoader()); + } catch (ClassNotFoundException ex) { + assert false : ex; + } + return DEFAULT; + } + + public static void setDefault(EnvironmentAccessor accessor) { + if (DEFAULT != null) { + throw new IllegalStateException(); + } + + DEFAULT = accessor; + } + + public abstract Environment createEnvironment(EnvironmentImplementation impl); +} diff --git a/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java b/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java --- a/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java +++ b/extexecution/src/org/netbeans/modules/extexecution/ProcessBuilderAccessor.java @@ -42,6 +42,7 @@ package org.netbeans.modules.extexecution; import org.netbeans.spi.extexecution.ProcessBuilderImplementation; +import org.netbeans.spi.extexecution.ProcessBuilderImplementation2; /** * @@ -78,4 +79,7 @@ public abstract org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder( ProcessBuilderImplementation impl, String description); + + public abstract org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder( + ProcessBuilderImplementation2 impl, String description); } diff --git a/extexecution/src/org/netbeans/modules/extexecution/ProcessParametersAccessor.java b/extexecution/src/org/netbeans/modules/extexecution/ProcessParametersAccessor.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/modules/extexecution/ProcessParametersAccessor.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.extexecution; + +import java.util.List; +import java.util.Map; +import org.netbeans.spi.extexecution.ProcessParameters; + +/** + * + * @author Petr Hejl + */ +public abstract class ProcessParametersAccessor { + + private static volatile ProcessParametersAccessor DEFAULT; + + public static ProcessParametersAccessor getDefault() { + ProcessParametersAccessor a = DEFAULT; + if (a != null) { + return a; + } + + // invokes static initializer of ProcessParameters.class + // that will assign value to the DEFAULT field above + Class c = ProcessParameters.class; + try { + Class.forName(c.getName(), true, c.getClassLoader()); + } catch (ClassNotFoundException ex) { + assert false : ex; + } + return DEFAULT; + } + + public static void setDefault(ProcessParametersAccessor accessor) { + if (DEFAULT != null) { + throw new IllegalStateException(); + } + + DEFAULT = accessor; + } + + public abstract ProcessParameters createProcessParameters(String executable, + String workingDirectory, List arguments, boolean redirectErrorStream, + Map environmentVariables); +} diff --git a/extexecution/src/org/netbeans/spi/extexecution/EnvironmentFactory.java b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentFactory.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentFactory.java @@ -0,0 +1,69 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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 2011 Sun Microsystems, Inc. + */ +package org.netbeans.spi.extexecution; + +import org.netbeans.api.extexecution.Environment; +import org.netbeans.modules.extexecution.EnvironmentAccessor; + +/** + * The factory allowing SPI implementors of {@link EnvironmentImplementation} + * to create its API instances {@link Environment}. + * + * @author Petr Hejl + * @since 1.37 + */ +public class EnvironmentFactory { + + private EnvironmentFactory() { + super(); + } + + /** + * Creates the instance of {@link Environment} from its SPI representation. + * + * @param impl SPI representation + * @return the API instance + */ + public static Environment createEnvironment(EnvironmentImplementation impl) { + return EnvironmentAccessor.getDefault().createEnvironment(impl); + } +} diff --git a/extexecution/src/org/netbeans/spi/extexecution/EnvironmentImplementation.java b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentImplementation.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/spi/extexecution/EnvironmentImplementation.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.spi.extexecution; + +import java.util.Map; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.extexecution.Environment; + +/** + * The interface representing the implementation + * of {@link org.netbeans.api.extexecution.Environment}. + * + * @see Environment + * @author Petr Hejl + * @since 1.37 + */ +public interface EnvironmentImplementation { + + /** + * Returns the value of the variable or null. + * + * @param name the name of the variable + * @return the value of the variable or null + */ + @CheckForNull + String getVariable(@NonNull String name); + + /** + * Appends a path to a path-like variable. The proper path separator should + * be used to separate the new value. + * + * @param name the name of the variable such as for example + * PATH or LD_LIBRARY_PATH + * @param value the value (path to append) + */ + void appendPath(@NonNull String name, @NonNull String value); + + /** + * Prepends a path to a path-like variable. The proper path separator should + * be used to separate the new value. + * + * @param name the name of the variable such as for example + * PATH or LD_LIBRARY_PATH + * @param value the value (path to prepend) + */ + void prependPath(@NonNull String name, @NonNull String value); + + /** + * Sets a value for a variable with the given name. + * + * @param name the name of the variable + * @param value the value + */ + void setVariable(@NonNull String name, @NonNull String value); + + /** + * Removes a variable with the given name. The subsequent call to + * {@link #getVariable(java.lang.String)} with the same argument should + * return null. + * + * @param name the name of the variable + */ + void removeVariable(@NonNull String name); + + /** + * Returns all variable names and associated values as a {@link Map}. + * Changes to the map must not be propagated back to the {@link Environment}. + * + * @return all variable names and associated values + */ + @NonNull + Map values(); + +} diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java --- a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java +++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderFactory.java @@ -63,9 +63,24 @@ * @param impl SPI representation * @param description human readable description of the builder * @return the API instance + * @deprecated use {@link #createProcessBuilder(org.netbeans.spi.extexecution.ProcessBuilderImplementation2, java.lang.String)} */ public static org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder( ProcessBuilderImplementation impl, String description) { return ProcessBuilderAccessor.getDefault().createProcessBuilder(impl, description); } + + /** + * Creates the instance of {@link org.netbeans.api.extexecution.ProcessBuilder} + * from its SPI representation. + * + * @param impl SPI representation + * @param description human readable description of the builder + * @return the API instance + * @since 1.37 + */ + public static org.netbeans.api.extexecution.ProcessBuilder createProcessBuilder( + ProcessBuilderImplementation2 impl, String description) { + return ProcessBuilderAccessor.getDefault().createProcessBuilder(impl, description); + } } diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java --- a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java +++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation.java @@ -62,6 +62,7 @@ * @see org.netbeans.api.extexecution.ProcessBuilder * @author Petr Hejl * @since 1.28 + * @deprecated use {@link ProcessBuilderImplementation2} */ public interface ProcessBuilderImplementation { @@ -80,7 +81,7 @@ * the process should be redirected to standard output stream * @return a process created with specified parameters and environment * configuration - * @throws IOException IOException if the process could not be created + * @throws IOException if the process could not be created * @throws UserQuestionException in case there is a need to interact with * user, don't be afraid to throw a subclass of * {@link UserQuestionException} with overriden {@link UserQuestionException#confirmed()} diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation2.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation2.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessBuilderImplementation2.java @@ -0,0 +1,93 @@ +/* + * 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.spi.extexecution; + +import java.io.IOException; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.extexecution.Environment; +import org.openide.util.Lookup; + +/** + * The interface representing the implementation + * of {@link org.netbeans.api.extexecution.ProcessBuilder}. + * + * @see org.netbeans.api.extexecution.ProcessBuilder + * @author Petr Hejl + * @since 1.37 + */ +public interface ProcessBuilderImplementation2 extends Lookup.Provider { + + /** + * Returns the object for environment variables manipulation. + * + * @return the object for environment variables manipulation + */ + @NonNull + Environment getEnvironment(); + + /** + * Provides an extension point to the implementors. One may enhance the + * functionality of {@link org.netbeans.api.extexecution.ProcessBuilder} + * by this as the content of this {@link Lookup} is included in + * {@link org.netbeans.api.extexecution.ProcessBuilder#getLookup()} + * + * @return a lookup providing an extension point + */ + @Override + Lookup getLookup(); + + /** + * Creates a process using the specified parameters. + *

+ * The environment variables stored in parameters are acquired by call to + * {@link Environment#values()}. So if the implementation does not aim to be + * or can't thread safe it may check or use the {@link Environment} + * directly. + * + * @param parameters the instance describing the process parameters + * @return a process created with specified parameters + * @throws IOException if the process could not be created + */ + @NonNull + Process createProcess(@NonNull ProcessParameters parameters) throws IOException; + +} diff --git a/extexecution/src/org/netbeans/spi/extexecution/ProcessParameters.java b/extexecution/src/org/netbeans/spi/extexecution/ProcessParameters.java new file mode 100644 --- /dev/null +++ b/extexecution/src/org/netbeans/spi/extexecution/ProcessParameters.java @@ -0,0 +1,142 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.spi.extexecution; + +import java.util.List; +import java.util.Map; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.modules.extexecution.ProcessParametersAccessor; + +/** + * The parameters configured for process creation. + * + * @see ProcessBuilderImplementation2 + * @author Petr Hejl + * @since 1.37 + */ +public final class ProcessParameters { + + private final String executable; + + private final String workingDirectory; + + private final List arguments; + + private final boolean redirectErrorStream; + + private final Map environmentVariables; + + static { + ProcessParametersAccessor.setDefault(new ProcessParametersAccessor() { + + @Override + public ProcessParameters createProcessParameters(String executable, String workingDirectory, + List arguments, boolean redirectErrorStream, Map environmentVariables) { + return new ProcessParameters(executable, workingDirectory, arguments, + redirectErrorStream, environmentVariables); + } + }); + } + + private ProcessParameters(String executable, String workingDirectory, List arguments, + boolean redirectErrorStream, Map environmentVariables) { + this.executable = executable; + this.workingDirectory = workingDirectory; + this.arguments = arguments; + this.redirectErrorStream = redirectErrorStream; + this.environmentVariables = environmentVariables; + } + + /** + * Returns the configured executable. + * + * @return the configured executable + */ + @NonNull + public String getExecutable() { + return executable; + } + + /** + * Returns the configured working directory or null in case it + * was not configured. + * + * @return the configured working directory or null in case it + * was not configured + */ + @CheckForNull + public String getWorkingDirectory() { + return workingDirectory; + } + + /** + * Returns the arguments configured for the process. + * + * @return the arguments configured for the process + */ + @NonNull + public List getArguments() { + return arguments; + } + + /** + * Returns true if standard error stream should be redirected + * to standard output stream. + * + * @return true if standard error stream should be redirected + * to standard output stream + */ + public boolean isRedirectErrorStream() { + return redirectErrorStream; + } + + /** + * Returns the environment variables configured for the process. + * + * @return the environment variables configured for the process + */ + @NonNull + public Map getEnvironmentVariables() { + return environmentVariables; + } +} diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java --- a/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java +++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/ExecutionServiceTest.java @@ -42,11 +42,14 @@ package org.netbeans.api.extexecution; +import java.io.ByteArrayInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.util.LinkedList; @@ -358,7 +361,7 @@ final String[] lines = new String[] {"Process line \u1234", "Process line \u1235", "Process line \u1236"}; TestInputStream is = new TestInputStream(TestInputUtils.prepareInputStream(lines, "\n", charset, true)); - TestProcess process = new TestProcess(0, is); + TestProcess process = new TestProcess(0, is, null); is.setProcess(process); TestCallable callable = new TestCallable(); @@ -536,6 +539,123 @@ assertFalse(val.get()); } + public void testWriterService() throws InterruptedException, ExecutionException { + TestProcess process = new TestProcess(0, + new ByteArrayInputStream("testing input".getBytes()), + new ByteArrayInputStream("testing error".getBytes())); + TestCallable callable = new TestCallable(); + callable.addProcess(process); + + StringWriter in = new StringWriter(); + StringWriter err = new StringWriter(); + ExecutionService service = ExecutionService.newService(callable, in, err); + Future task = service.run(); + assertNotNull(task); + + process.waitStarted(); + + process.destroy(); + process.waitFor(); + assertTrue(process.isFinished()); + + assertEquals(0, task.get().intValue()); + assertEquals("testing input", in.toString()); + assertEquals("testing error", err.toString()); + } + + public void testWriterServiceOnce() throws InterruptedException, ExecutionException { + TestProcess process = new TestProcess(0, + new ByteArrayInputStream(new byte[0]), null); + TestCallable callable = new TestCallable(); + callable.addProcess(process); + + ExecutionService service = ExecutionService.newService(callable, + (Writer) null, (Writer) null); + Future task = service.run(); + assertNotNull(task); + + process.waitStarted(); + + process.destroy(); + process.waitFor(); + assertTrue(process.isFinished()); + + assertEquals(0, task.get().intValue()); + + try { + service.run(); + fail("Multiple run should fail with ISE"); + } catch (IllegalStateException ex) { + // expected + } + } + + public void testLineService() throws InterruptedException, ExecutionException { + final String[] linesIn = new String[] {"Process line 1", "Process line 2", "Process line 3"}; + final String[] linesErr = new String[] {"Error line 1", "Error line 2", "Error line 3"}; + + TestInputStream in = new TestInputStream( + TestInputUtils.prepareInputStream(linesIn, "\n", Charset.defaultCharset(), true)); + TestInputStream err = new TestInputStream( + TestInputUtils.prepareInputStream(linesErr, "\n", Charset.defaultCharset(), true)); + TestProcess process = new TestProcess(0, in, err); + TestCallable callable = new TestCallable(); + callable.addProcess(process); + + TestLineProcessor inProcessor = new TestLineProcessor(false); + TestLineProcessor errProcessor = new TestLineProcessor(false); + ExecutionService service = ExecutionService.newService(callable, + inProcessor, errProcessor, null); + Future task = service.run(); + assertNotNull(task); + + process.waitStarted(); + + process.destroy(); + process.waitFor(); + assertTrue(process.isFinished()); + + assertEquals(0, task.get().intValue()); + + List processed = inProcessor.getLinesProcessed(); + assertEquals(linesIn.length, processed.size()); + for (int i = 0; i < linesIn.length; i++) { + assertEquals(linesIn[i], processed.get(i)); + } + processed = errProcessor.getLinesProcessed(); + assertEquals(linesErr.length, processed.size()); + for (int i = 0; i < linesErr.length; i++) { + assertEquals(linesErr[i], processed.get(i)); + } + } + + public void testLineServiceOnce() throws InterruptedException, ExecutionException { + TestProcess process = new TestProcess(0, + new ByteArrayInputStream(new byte[0]), null); + TestCallable callable = new TestCallable(); + callable.addProcess(process); + + ExecutionService service = ExecutionService.newService(callable, + null, null, null); + Future task = service.run(); + assertNotNull(task); + + process.waitStarted(); + + process.destroy(); + process.waitFor(); + assertTrue(process.isFinished()); + + assertEquals(0, task.get().intValue()); + + try { + service.run(); + fail("Multiple run should fail with ISE"); + } catch (IllegalStateException ex) { + // expected + } + } + private static InputOutputManager.InputOutputData getInputOutput(String name, boolean actions, String optionsPath) { @@ -579,6 +699,8 @@ private final InputStream is; + private final InputStream err; + private boolean finished; private boolean started; @@ -586,12 +708,13 @@ public TestProcess(int returnValue) { this(returnValue, TestInputUtils.prepareInputStream( new String[] {"Process line 1", "Process line 2", "Process line 3"}, "\n", - Charset.defaultCharset(), true)); + Charset.defaultCharset(), true), null); } - public TestProcess(int returnValue, InputStream is) { + public TestProcess(int returnValue, InputStream is, InputStream err) { this.returnValue = returnValue; this.is = is; + this.err = err; } public void start() { @@ -637,6 +760,9 @@ @Override public InputStream getErrorStream() { + if (err != null) { + return err; + } return new InputStream() { @Override public int read() throws IOException { diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java --- a/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java +++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/ProcessBuilderTest.java @@ -48,8 +48,13 @@ import java.util.List; import java.util.Map; import org.netbeans.junit.NbTestCase; +import org.netbeans.spi.extexecution.EnvironmentFactory; +import org.netbeans.spi.extexecution.EnvironmentImplementation; import org.netbeans.spi.extexecution.ProcessBuilderFactory; import org.netbeans.spi.extexecution.ProcessBuilderImplementation; +import org.netbeans.spi.extexecution.ProcessBuilderImplementation2; +import org.netbeans.spi.extexecution.ProcessParameters; +import org.openide.util.Lookup; /** * @@ -166,7 +171,7 @@ assertEquals("test2", testBuilder.getPaths().get(0)); } - public void testEnvironment() throws IOException { + public void testEnvironmentVariables() throws IOException { TestProcessBuilder testBuilder = new TestProcessBuilder(); ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder"); builder.setExecutable("ls"); @@ -201,6 +206,136 @@ assertEquals("value2", testBuilder.getEnvironment().get("key2")); } + public void testEnvironment() throws IOException { + TestProcessBuilder2 testBuilder = new TestProcessBuilder2(); + ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder"); + builder.setExecutable("ls"); + + builder.getEnvironment().setVariable("key1", "value1"); + builder.getEnvironment().setVariable("key2", "value2"); + + assertEquals("value1", testBuilder.getEnvironment().getVariable("key1")); + assertEquals("value2", testBuilder.getEnvironment().getVariable("key2")); + + builder.call(); + assertEquals(2, testBuilder.getParameters().getEnvironmentVariables().size()); + assertEquals("value1", testBuilder.getParameters().getEnvironmentVariables().get("key1")); + assertEquals("value2", testBuilder.getParameters().getEnvironmentVariables().get("key2")); + + builder.getEnvironment().prependPath("PATH", "/test1"); + builder.getEnvironment().prependPath("PATH", "/test2"); + + builder.call(); + assertEquals(3, testBuilder.getParameters().getEnvironmentVariables().size()); + assertEquals("/test2:/test1", + testBuilder.getParameters().getEnvironmentVariables().get("PATH")); + + builder.getEnvironment().appendPath("PATH", "/test3"); + + builder.call(); + assertEquals(3, testBuilder.getParameters().getEnvironmentVariables().size()); + assertEquals("/test2:/test1:/test3", + testBuilder.getParameters().getEnvironmentVariables().get("PATH")); + + builder.getEnvironment().removeVariable("PATH"); + assertNull(builder.getEnvironment().getVariable("PATH")); + + builder.call(); + assertEquals(2, testBuilder.getParameters().getEnvironmentVariables().size()); + assertNull(testBuilder.getParameters().getEnvironmentVariables().get("PATH")); + } + + public void testFallbackEnvironment() throws IOException { + TestProcessBuilder testBuilder = new TestProcessBuilder(); + ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder"); + builder.setExecutable("ls"); + + builder.getEnvironment().setVariable("key1", "value1"); + builder.getEnvironment().setVariable("key2", "value2"); + + builder.call(); + assertEquals(2, testBuilder.getEnvironment().size()); + assertEquals("value1", testBuilder.getEnvironment().get("key1")); + assertEquals("value2", testBuilder.getEnvironment().get("key2")); + + builder.getEnvironment().prependPath("PATH", "/test1"); + builder.getEnvironment().prependPath("Path", "/test2"); + + builder.call(); + assertEquals(2, testBuilder.getPaths().size()); + assertEquals("/test2", testBuilder.getPaths().get(0)); + assertEquals("/test1", testBuilder.getPaths().get(1)); + + try { + builder.getEnvironment().prependPath("LD_LIBRARY_PATH", "/test"); + fail("Prepend of unknown path does not throw exception"); + } catch (UnsupportedOperationException ex) { + // expected + } + + try { + builder.getEnvironment().appendPath("PATH", "/test"); + fail("Append of any path does not throw exception"); + } catch (UnsupportedOperationException ex) { + // expected + } + + try { + builder.getEnvironment().getVariable("PATH"); + fail("getVariable() does not throw exception"); + } catch (UnsupportedOperationException ex) { + // expected + } + + try { + builder.getEnvironment().removeVariable("PATH"); + fail("removeVariable() does not throw exception"); + } catch (UnsupportedOperationException ex) { + // expected + } + + try { + builder.getEnvironment().values(); + fail("values() does not throw exception"); + } catch (UnsupportedOperationException ex) { + // expected + } + } + + public void testFallbackEnvironmentCombined() throws IOException { + TestProcessBuilder testBuilder = new TestProcessBuilder(); + ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder"); + builder.setExecutable("ls"); + + builder.getEnvironment().setVariable("key1", "value1"); + builder.getEnvironment().setVariable("key2", "value2"); + + builder.call(); + assertEquals(2, testBuilder.getEnvironment().size()); + assertEquals("value1", testBuilder.getEnvironment().get("key1")); + assertEquals("value2", testBuilder.getEnvironment().get("key2")); + + builder.setEnvironmentVariables(Collections.singletonMap("key3", "value3")); + + builder.call(); + assertEquals(1, testBuilder.getEnvironment().size()); + assertEquals("value3", testBuilder.getEnvironment().get("key3")); + + builder.getEnvironment().prependPath("PATH", "/test1"); + builder.getEnvironment().prependPath("Path", "/test2"); + + builder.call(); + assertEquals(2, testBuilder.getPaths().size()); + assertEquals("/test2", testBuilder.getPaths().get(0)); + assertEquals("/test1", testBuilder.getPaths().get(1)); + + builder.setPaths(Collections.singletonList("/test3")); + + builder.call(); + assertEquals(1, testBuilder.getPaths().size()); + assertEquals("/test3", testBuilder.getPaths().get(0)); + } + public void testRedirectErrorStream() throws IOException { TestProcessBuilder testBuilder = new TestProcessBuilder(); ProcessBuilder builder = ProcessBuilderFactory.createProcessBuilder(testBuilder, "Test builder"); @@ -216,7 +351,7 @@ assertTrue(testBuilder.isRedirectErrorStream()); } - private class TestProcessBuilder implements ProcessBuilderImplementation { + private static class TestProcessBuilder implements ProcessBuilderImplementation { private String executable; @@ -267,6 +402,80 @@ public String getWorkingDirectory() { return workingDirectory; } + } + private static class TestProcessBuilder2 implements ProcessBuilderImplementation2 { + + private final Environment environment = EnvironmentFactory.createEnvironment(new TestEnvironment()); + + private ProcessParameters parameters; + + @Override + public Environment getEnvironment() { + return environment; + } + + @Override + public Lookup getLookup() { + return Lookup.EMPTY; + } + + @Override + public Process createProcess(ProcessParameters parameters) throws IOException { + this.parameters = parameters; + + return null; + } + + public ProcessParameters getParameters() { + return parameters; + } + } + + private static class TestEnvironment implements EnvironmentImplementation { + + private final Map values = new HashMap(); + + @Override + public String getVariable(String name) { + return values.get(name); + } + + @Override + public void appendPath(String name, String value) { + String orig = values.get(name); + if (orig == null || orig.isEmpty()) { + values.put(name, value); + } else { + // intentionally hardcoded for tests + values.put(name, orig + ":" + value); + } + } + + @Override + public void prependPath(String name, String value) { + String orig = values.get(name); + if (orig == null || orig.isEmpty()) { + values.put(name, value); + } else { + // intentionally hardcoded for tests + values.put(name, value + ":" + orig); + } + } + + @Override + public void setVariable(String name, String value) { + values.put(name, value); + } + + @Override + public void removeVariable(String name) { + values.remove(name); + } + + @Override + public Map values() { + return new HashMap(values); + } } } diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java --- a/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java +++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/input/InputReadersReaderTest.java @@ -47,6 +47,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.io.StringReader; import java.nio.charset.Charset; import java.util.Arrays; import org.netbeans.junit.NbTestCase; @@ -85,4 +86,22 @@ assertTrue(Arrays.equals(TEST_CHARS, processor.getCharsProcessed())); } + + public void testReadStringReader() throws IOException { + Reader reader = new StringReader(new String(TEST_CHARS)); + InputReader inputReader = InputReaders.forReader(reader); + TestInputProcessor processor = new TestInputProcessor(false); + + int read = 0; + int retries = 0; + while (read < TEST_CHARS.length && retries < MAX_RETRIES) { + read += inputReader.readInput(processor); + retries++; + } + + assertEquals(read, TEST_CHARS.length); + assertEquals(0, processor.getResetCount()); + + assertTrue(Arrays.equals(TEST_CHARS, processor.getCharsProcessed())); + } } diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessCharsetTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessCharsetTest.java new file mode 100644 --- /dev/null +++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessCharsetTest.java @@ -0,0 +1,91 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.api.extexecution.process; + +import java.nio.charset.Charset; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Petr Hejl + */ +public class ProcessCharsetTest extends NbTestCase { + + public ProcessCharsetTest(String name) { + super(name); + } + + public void testProcessCharset() { + assertFalse(ProcessCharset.isSupported(new TestProcess(Lookup.EMPTY))); + + assertNull(ProcessCharset.getOutputCharset(new TestProcess(Lookup.EMPTY))); + assertNull(ProcessCharset.getInputCharset(new TestProcess(Lookup.EMPTY))); + assertNull(ProcessCharset.getErrorCharset(new TestProcess(Lookup.EMPTY))); + + TestProcessCharset charset = new TestProcessCharset(); + TestProcess process = new TestProcess(Lookups.fixed(charset)); + assertTrue(ProcessCharset.isSupported(process)); + assertEquals(charset.getOutputCharset(), ProcessCharset.getOutputCharset(process)); + assertEquals(charset.getInputCharset(), ProcessCharset.getInputCharset(process)); + assertEquals(charset.getErrorCharset(), ProcessCharset.getErrorCharset(process)); + } + + private static class TestProcessCharset extends ProcessCharset { + + @Override + protected Charset getInputCharset() { + return Charset.forName("US-ASCII"); + } + + @Override + protected Charset getOutputCharset() { + return Charset.forName("ISO-8859-1"); + } + + @Override + protected Charset getErrorCharset() { + return Charset.forName("UTF-8"); + } + } +} diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessIdTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessIdTest.java new file mode 100644 --- /dev/null +++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessIdTest.java @@ -0,0 +1,76 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.api.extexecution.process; + +import org.netbeans.junit.NbTestCase; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Petr Hejl + */ +public class ProcessIdTest extends NbTestCase { + + public ProcessIdTest(String name) { + super(name); + } + + public void testProcessId() { + assertFalse(ProcessId.isSupported(new TestProcess(Lookup.EMPTY))); + + assertNull(ProcessId.getId(new TestProcess(Lookup.EMPTY))); + + TestProcessId id = new TestProcessId(); + TestProcess process = new TestProcess(Lookups.fixed(id)); + assertTrue(ProcessId.isSupported(process)); + assertEquals(id.getId(), ProcessId.getId(process)); + } + + private static class TestProcessId extends ProcessId { + + @Override + protected Integer getId() { + return 1; + } + } +} diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessSignalTest.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessSignalTest.java new file mode 100644 --- /dev/null +++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/ProcessSignalTest.java @@ -0,0 +1,116 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.api.extexecution.process; + +import java.io.IOException; +import org.netbeans.junit.NbTestCase; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Petr Hejl + */ +public class ProcessSignalTest extends NbTestCase { + + public ProcessSignalTest(String name) { + super(name); + } + + public void testProcessSignal() throws IOException { + assertFalse(ProcessSignal.isSupported(new TestProcess(Lookup.EMPTY))); + + TestProcessSignal signal = new TestProcessSignal(); + TestProcess process = new TestProcess(Lookups.fixed(signal)); + assertTrue(ProcessSignal.isSupported(process)); + + ProcessSignal.signal(process, ProcessSignal.Signal.SIGALRM); + ProcessSignal.signalGroup(process, ProcessSignal.Signal.SIGCANCEL); + + assertEquals(ProcessSignal.Signal.SIGALRM, signal.getSignal()); + assertEquals(ProcessSignal.Signal.SIGCANCEL, signal.getGroupSignal()); + + try { + ProcessSignal.signal(process, ProcessSignal.Signal.NULL); + fail("No exception propagated"); + } catch (IOException ex) { + // expected + } + try { + ProcessSignal.signalGroup(process, ProcessSignal.Signal.NULL); + fail("No exception propagated"); + } catch (IOException ex) { + // expected + } + } + + private static class TestProcessSignal extends ProcessSignal { + + private Signal signal; + + private Signal groupSignal; + + @Override + protected void signal(Signal signal) throws IOException { + if (this.signal != null) { + throw new IOException("Test"); + } + this.signal = signal; + } + + @Override + protected void signalGroup(Signal signal) throws IOException { + if (this.groupSignal != null) { + throw new IOException("Test"); + } + this.groupSignal = signal; + } + + public Signal getSignal() { + return signal; + } + + public Signal getGroupSignal() { + return groupSignal; + } + } +} diff --git a/extexecution/test/unit/src/org/netbeans/api/extexecution/process/TestProcess.java b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/TestProcess.java new file mode 100644 --- /dev/null +++ b/extexecution/test/unit/src/org/netbeans/api/extexecution/process/TestProcess.java @@ -0,0 +1,94 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.api.extexecution.process; + +import java.io.InputStream; +import java.io.OutputStream; +import org.openide.util.Lookup; + +/** + * + * @author Petr Hejl + */ +public class TestProcess extends Process implements Lookup.Provider { + + private final Lookup lookup; + + public TestProcess(Lookup lookup) { + this.lookup = lookup; + } + + @Override + public Lookup getLookup() { + return lookup; + } + + @Override + public OutputStream getOutputStream() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public InputStream getInputStream() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public InputStream getErrorStream() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int waitFor() throws InterruptedException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int exitValue() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void destroy() { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/extexecution/test/unit/src/org/netbeans/spi/extexecution/ProcessParametersTest.java b/extexecution/test/unit/src/org/netbeans/spi/extexecution/ProcessParametersTest.java new file mode 100644 --- /dev/null +++ b/extexecution/test/unit/src/org/netbeans/spi/extexecution/ProcessParametersTest.java @@ -0,0 +1,79 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 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 2013 Sun Microsystems, Inc. + */ +package org.netbeans.spi.extexecution; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.extexecution.ProcessParametersAccessor; + +/** + * + * @author Petr Hejl + */ +public class ProcessParametersTest extends NbTestCase { + + public ProcessParametersTest(String name) { + super(name); + } + + public void testParameters() { + Map variables = new HashMap(); + variables.put("key1", "value1"); + variables.put("key2", "value2"); + + ProcessParameters params = ProcessParametersAccessor.getDefault().createProcessParameters( + "ls", "/home", Collections.singletonList("argument"), true, variables); + + assertEquals("ls", params.getExecutable()); + assertEquals("/home", params.getWorkingDirectory()); + assertTrue(params.isRedirectErrorStream()); + + assertEquals(1, params.getArguments().size()); + assertEquals("argument", params.getArguments().get(0)); + + assertEquals(2, params.getEnvironmentVariables().size()); + assertEquals("value1", params.getEnvironmentVariables().get("key1")); + assertEquals("value2", params.getEnvironmentVariables().get("key2")); + } +} diff --git a/j2ee.weblogic9/nbproject/project.xml b/j2ee.weblogic9/nbproject/project.xml --- a/j2ee.weblogic9/nbproject/project.xml +++ b/j2ee.weblogic9/nbproject/project.xml @@ -105,7 +105,7 @@ 2 - 1.13 + 1.37 diff --git a/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java b/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java --- a/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java +++ b/j2ee.weblogic9/src/org/netbeans/modules/j2ee/weblogic9/deploy/CommandBasedDeployer.java @@ -66,12 +66,10 @@ import javax.enterprise.deploy.spi.Target; import javax.enterprise.deploy.spi.TargetModuleID; import javax.enterprise.deploy.spi.status.ProgressObject; -import org.netbeans.api.extexecution.ExecutionDescriptor; import org.netbeans.api.extexecution.ExecutionService; import org.netbeans.api.extexecution.ExternalProcessBuilder; -import org.netbeans.api.extexecution.input.InputProcessor; -import org.netbeans.api.extexecution.input.InputProcessors; import org.netbeans.api.extexecution.input.LineProcessor; +import org.netbeans.api.extexecution.input.LineProcessors; import org.netbeans.api.java.platform.JavaPlatform; import org.netbeans.api.java.platform.JavaPlatformManager; import org.netbeans.modules.j2ee.dd.api.application.Application; @@ -88,14 +86,12 @@ import org.netbeans.modules.j2ee.weblogic9.config.WLMessageDestination; import org.netbeans.modules.j2ee.weblogic9.dd.model.WebApplicationModel; import org.netbeans.modules.j2ee.weblogic9.ui.FailedAuthenticationSupport; -import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.filesystems.JarFileSystem; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; import org.openide.util.Utilities; -import org.openide.windows.InputOutput; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -110,8 +106,6 @@ private static final RequestProcessor URL_WAIT_RP = new RequestProcessor("Weblogic URL Wait", 10); // NOI18N - private static final boolean SHOW_CONSOLE = Boolean.getBoolean(CommandBasedDeployer.class.getName() + ".showConsole");; - public CommandBasedDeployer(WLDeploymentManager deploymentManager) { super(deploymentManager); } @@ -668,19 +662,25 @@ builder = builder.addArgument(param); } - ExecutionDescriptor descriptor = new ExecutionDescriptor().inputVisible(true).outLineBased(true); - if (processor != null) { - descriptor = descriptor.outProcessorFactory(new ExecutionDescriptor.InputProcessorFactory() { + LineProcessor realProcessor = processor; + if (LOGGER.isLoggable(Level.FINE)) { + realProcessor = LineProcessors.proxy(processor, new LineProcessor() { - public InputProcessor newInputProcessor(InputProcessor defaultProcessor) { - return InputProcessors.proxy(defaultProcessor, InputProcessors.bridge(processor)); + @Override + public void processLine(String line) { + LOGGER.log(Level.FINE, line); + } + + @Override + public void reset() { + } + + @Override + public void close() { } }); } - if (!SHOW_CONSOLE) { - descriptor = descriptor.inputOutput(InputOutput.NULL); - } - return ExecutionService.newService(builder, descriptor, "weblogic.Deployer " + command); + return ExecutionService.newService(builder, realProcessor, null, null); } private String getClassPath() { 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 @@ -88,6 +88,7 @@ org.netbeans.modules.masterfs.macosx org.netbeans.modules.masterfs.solaris org.netbeans.modules.masterfs.windows + org.netbeans.modules.nativeexecution.impl org.netbeans.modules.python.qshell com.sun.jna com.sun.jna.ptr 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,115 @@ + + + org.netbeans.modules.apisupport.project + + + org.netbeans.modules.nativeexecution.impl + + + com.jcraft.jsch + + + + 0.1.43 + + + + org.netbeans.libs.jna + + + + 1 + 1.4 + + + + org.netbeans.modules.cnd.execution + + + + + + + + + org.netbeans.modules.dlight.nativeexecution + + + + 1.26 + + + + org.netbeans.modules.dlight.terminal + + + + 1.14 + + + + org.netbeans.modules.extexecution + + + + 2 + 1.37 + + + + org.openide.modules + + + + 7.35 + + + + org.openide.util + + + + 8.24 + + + + org.openide.util.lookup + + + + 8.15 + + + + + + unit + + org.netbeans.bootstrap + + + + org.netbeans.core.startup + + + + org.netbeans.insane + + + + org.netbeans.libs.junit4 + + + + org.netbeans.modules.nbjunit + + + + + + + + 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,198 @@ +/* + * 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.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.NativeProcessImplementation; +import org.netbeans.modules.cnd.execution.spi.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 + } + + 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/HelperUtility.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/HelperUtility.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/common/HelperUtility.java @@ -0,0 +1,169 @@ +/* + * 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 2010 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.common; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.HashMap; +import java.util.MissingResourceException; +import java.util.logging.Level; +import org.netbeans.modules.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.nativeexecution.impl.util.FileOperationsSupport; +import org.netbeans.modules.cnd.execution.util.HostInfo; +import org.openide.modules.InstalledFileLocator; +import org.openide.util.Lookup; + +/** + * + * @author ak119685 + */ +public class HelperUtility { + + protected static final ExecutionLogger log = ExecutionLogger.getInstance(); + private final HashMap cache = new HashMap(); + private final String pattern; + protected final String codeNameBase; + + public HelperUtility(String searchPattern) { + this("org.netbeans.modules.nativeexecution.impl.common", searchPattern); // NOI18N + } + + public HelperUtility(String codeNameBase, String searchPattern) { + this.codeNameBase = codeNameBase; + pattern = searchPattern; + } + + /** + * + * @param connection + * @return the ready-to-use remote path for the utility + * @throws IOException + */ + public final String getPath(final Connection connection) throws IOException { + return getPath(connection, null); + } + + private String getPath(final Connection connection, final HostInfo hostInfo) throws IOException { + if (!connection.isConnected()) { + throw new IllegalStateException("Connection " + // NOI18N + connection.getURI() + " is broken"); // NOI18N + } + + String result; + + synchronized (cache) { + result = cache.get(connection.getURI()); + + if (result == null) { + ConnectionAccessor access = ConnectionAccessor.getDefault(); + Lookup lookup = access.getConnectionLookup(connection); + FileOperationsSupport fos = lookup.lookup(FileOperationsSupport.class); + if (fos == null) { + log.log(Level.WARNING, + "FileOperationsSupport is not provided for {0}", + connection.getURI()); // NOI18N + return null; + } + + final HostInfo info = (hostInfo == null) + ? HostInfo.getFor(connection) + : hostInfo; + + if (!isSupported(info)) { + log.log(Level.INFO, "Utility {0} is not supported for {1}", + new Object[]{pattern, connection.getURI()}); // NOI18N + return null; + } + + try { + File localFile = getLocalFileFor(info); + + // Construct destination: {tmpbase}/{hash}/{name} + + // hash - a string unique to pair: + // local file location and connection + String key = localFile.getAbsolutePath().concat(connection.getURI().toString()); + String hash = Integer.toString(key.hashCode()).replace('-', '0'); + + + String fileName = localFile.getName(); + String dstFile = info.getTempDir() + '/' + hash + '/' + fileName; + + result = fos.uploadFile(localFile, dstFile, 0700); + + cache.put(connection.getURI(), result); + } catch (MissingResourceException ex) { + return null; + } catch (IOException ex) { + throw ex; + } catch (Exception ex) { + if (ex.getCause() instanceof IOException) { + throw (IOException) ex.getCause(); + } + throw new IOException(ex); + } + } + } + + return result; + } + + protected File getLocalFileFor(final HostInfo info) throws MissingResourceException { + String path = info.expandMacros(pattern); + + InstalledFileLocator fl = InstalledFileLocator.getDefault(); + File file = fl.locate(path, codeNameBase, false); + + if (file == null || !file.exists()) { + throw new MissingResourceException(path, null, null); //NOI18N + } + + return file; + } + + protected boolean isSupported(HostInfo hostInfo) { + return true; + } +} 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,198 @@ +/* + * 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.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.netbeans.api.extexecution.ExecutionService; +import org.netbeans.api.extexecution.input.LineProcessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfoProvider; +import org.openide.util.Exceptions; + +/** + * + * @author Andrew + */ +public final class ShellBasedConnectionInfoProvider implements ConnectionInfoProvider { + @Override + public ConnectionInfo getConnectionInfo(Connection connection) throws NativeExecutionException { + NativeProcessBuilder npb = connection.newNativeProcessBuilder(); + npb.setShellScript("/bin/sh", "/bin/sh -s"); // NOI18N + final Map env = new HashMap(); + final Map info = new HashMap(); + + // FIXME change this to piped/proxy reader/writer + StringBuilder sb = new StringBuilder(); + sb.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)); + try { + String line; + while ((line = br.readLine()) != null) { + sb.append(line).append('\n'); + } + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } finally { + try { + br.close(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + Reader processInput = new StringReader(sb.toString()); + try { + ExecutionService.newService(npb, new LineProcessor() { + + 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(String 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: + info.put(name, value); + break; + } + } + @Override + public void reset() { + } + + @Override + public void close() { + } + }, null, processInput).run().get(); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new NativeExecutionException(ex); + } catch (ExecutionException ex) { + throw new NativeExecutionException(ex); + } + + return new ConnectionInfo() { + + @Override + public Map getEnvironmentMap() { + return env; + } + + @Override + public Map getProperties() { + return info; + } + }; + } + + /** + * @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. + +MSG_PasswordInteractive={0} {1} +JSchPasswordPromptPanel.pwdField.text=jPasswordField1 diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchChannelsSupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchChannelsSupport.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchChannelsSupport.java @@ -0,0 +1,349 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 2010 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UserInfo; +import java.io.InterruptedIOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +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.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.api.ConnectionException; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.cnd.execution.spi.UserInteraction; +import org.openide.util.Exceptions; + +/** + * + * @author ak119685 + */ +public final class JSchChannelsSupport { + + 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 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 UserInfo userInfo; + 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 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(); + + static { + Set> data = new HashSet>(System.getProperties().entrySet()); + + for (Entry prop : data) { + String var = prop.getKey().toString(); + String val = prop.getValue().toString(); + if (var != null && val != null) { + if (var.startsWith("jsch.session.cfg.")) { // NOI18N + jschSessionConfig.put(var.substring(17), val); + } + if (var.startsWith("jsch.cfg.")) { // NOI18N + JSch.setConfig(var.substring(9), val); + jschSessionConfig.put(var.substring(9), val); + } + } + } + } + private final URI uri; + + private JSchChannelsSupport(JSch jsch, URI uri, String user, String host, Integer port, UserInfo userInfo) { + this.jsch = jsch; + this.uri = uri; + this.user = user; + this.host = host; + this.port = port; + this.userInfo = userInfo; + } + + 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); + UserInteraction userInteraction = dataProvider.getProperty(uri, UserInteraction.class, AuthDataProvider.USER_INTERACTION); + UserInfo userInfo_ = new RemoteUserInfo(uri, dataProvider, userInteraction); + JSch jsch_ = new JSch(); + initJsch(jsch_, uri, dataProvider); + + return new JSchChannelsSupport(jsch_, uri, user_, host_, port_, userInfo_); + } + + 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, InterruptedIOException { + JSchException exception = null; + + for (int i = 0; i < JSCH_CONNECTION_RETRY; i++) { + Session session = findFreeSession(); + + if (session == null) { + if (sessions.size() >= JSCH_SESSIONS_PER_ENV) { + if (waitIfNoAvailable) { + try { + sessionsLock.lock(); + while (session == null) { + try { + sessionAvailable.await(); + } catch (InterruptedException ex) { + Thread.interrupted(); + throw new InterruptedIOException(uri.toString()); + } + session = findFreeSession(); + } + } finally { + sessionsLock.unlock(); + } + } else { + throw new ConnectionException(uri, "All " + JSCH_SESSIONS_PER_ENV + " sessions for " + host + " are fully loaded"); // NOI18N + } + } + } + + try { + if (session == null) { + session = startNewSession(true); + } + + Channel result = session.openChannel(type); + if (result != null) { + log.log(Level.FINE, "Acquired channel [{0}] from session [{1}].", new Object[]{System.identityHashCode(result), System.identityHashCode(session)}); // NOI18N + + knownChannels.add(result); + + return result; + } + } catch (JSchException ex) { + exception = ex; + } + + if (session != null && !session.isConnected()) { + sessions.remove(session); + } + } + + // It is either JSCH_CONNECTION_RETRY times we got JSchException => + // exception is set; or there was another exception => it was thrown + // already + assert exception != null; + throw new ConnectionException(uri, exception); + } + + public boolean isConnected() { + // ConcurrentHashMap.keySet() never throws ConcurrentModificationException + for (Session s : sessions.keySet()) { + if (s.isConnected()) { + return true; + } + } + + return false; + } + +// public synchronized void reconnect(ExecutionEnvironment env) throws ConnectionException { +// disconnect(); +// connect(); +// } + private Session findFreeSession() { + for (Entry entry : sessions.entrySet()) { + Session s = entry.getKey(); + AtomicInteger availableChannels = entry.getValue(); + if (s.isConnected() && availableChannels.get() > 0) { + log.log(Level.FINE, "availableChannels == {0}", new Object[]{availableChannels.get()}); // NOI18N + int remains = availableChannels.decrementAndGet(); + log.log(Level.FINE, "Reuse session [{0}]. {1} channels remains...", new Object[]{System.identityHashCode(s), remains}); // NOI18N + return s; + } + } + + return null; + } + + public synchronized void connect() throws ConnectionException, InterruptedIOException { + if (isConnected()) { + return; + } + + startNewSession(false); + } + + public synchronized void disconnect() { + for (Session s : sessions.keySet()) { + s.disconnect(); + } + } + + private Session startNewSession(boolean acquireChannel) throws InterruptedIOException, ConnectionException { + Session newSession = null; + try { + newSession = jsch.getSession(user, host, port); + 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); + } catch (JSchException ex) { + if (ex.getCause() instanceof InterruptedIOException) { + throw new InterruptedIOException(uri.toString()); + } + throw new ConnectionException(uri, ex); + } finally { + // TODO: notify that password could be eraised + } + + 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; + } + + public synchronized void releaseChannel(final Channel channel) throws JSchException { + if (!knownChannels.remove(channel)) { + // Means it was not in the collection + return; + } + + Session s = channel.getSession(); + + log.log(Level.FINE, "Releasing channel [{0}] for session [{1}].", new Object[]{System.identityHashCode(channel), System.identityHashCode(s)}); // NOI18N + channel.disconnect(); + + int count = sessions.get(s).incrementAndGet(); + + List sessionsToRemove = new ArrayList(); + + if (count == JSCH_CHANNELS_PER_SESSION) { + // No more channels in this session ... + // Do we have other ready-to-serve sessions? + // In this case will close this one. + for (Entry entry : sessions.entrySet()) { + if (entry.getKey() == s) { + continue; + } + if (entry.getValue().get() > 0) { + log.log(Level.FINE, "Found another session [{0}] with {1} free slots. Will remove this one [{2}].", // NOI18N + new Object[]{ + System.identityHashCode(entry.getKey()), + entry.getValue().get(), + System.identityHashCode(s)}); + + sessionsToRemove.add(s); + break; + } + } + } else { + // This sessions is capable to provide a channel on next request + // Perhaps we have empty sessions that can be closed then? + for (Entry entry : sessions.entrySet()) { + if (entry.getKey() == s) { + continue; + } + + if (entry.getValue().get() == JSCH_CHANNELS_PER_SESSION) { + log.log(Level.FINE, "Found empty session [{0}] while this one is also has free slots [{1}].", // NOI18N + new Object[]{ + System.identityHashCode(entry.getKey()), + System.identityHashCode(s)}); + sessionsToRemove.add(entry.getKey()); + } + } + } + + for (Session sr : sessionsToRemove) { + log.log(Level.FINE, "Closing session [{0}].", new Object[]{System.identityHashCode(s)}); // NOI18N + sr.disconnect(); + sessions.remove(sr); + } + + try { + sessionsLock.lock(); + sessionAvailable.signalAll(); + } finally { + sessionsLock.unlock(); + } + } +} 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,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.impl.jsch; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.ChannelShell; +import com.jcraft.jsch.JSchException; +import java.io.InterruptedIOException; +import java.net.URI; +import org.netbeans.modules.cnd.execution.api.BrokenConnectionException; +import org.netbeans.modules.cnd.execution.api.ConnectionException; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.common.ConnectionsInfo; +import org.netbeans.modules.nativeexecution.impl.common.ShellBasedConnectionInfoProvider; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.impl.nbstart.NbStartProcessCreator; +import org.netbeans.modules.nativeexecution.impl.nbstart.NbStartUtility; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author akrasny + */ +public final 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( + new JSchFileOperationsSupport(this), + new ShellBasedConnectionInfoProvider()); + } + + @Override + public NativeProcessCreator newProcessBuilderImpl() throws NativeExecutionException { + if (!cs.isConnected()) { + throw new BrokenConnectionException(uri); + } + + if (NbStartUtility.isSupported(ConnectionsInfo.getConnectionInfo(uri))) { + return new NbStartProcessCreator(); + } else { + return new JSchProcessCreator(this); + } + } + + @Override + public boolean isConnected() { + return cs.isConnected(); + } + + @SuppressWarnings("Unchecked") + ChannelExec getChannelExec(boolean waitIfNoAvailable) throws ConnectionException, InterruptedIOException { + return (ChannelExec) cs.acquireChannel("exec", waitIfNoAvailable); // NOI18N + } + + @SuppressWarnings("Unchecked") + ChannelShell getChannelShell(boolean waitIfNoAvailable) throws ConnectionException, InterruptedIOException { + return (ChannelShell) cs.acquireChannel("shell", waitIfNoAvailable); // NOI18N + } + + @SuppressWarnings("Unchecked") + ChannelSftp getChannelSftp(boolean waitIfNoAvailable) throws ConnectionException, InterruptedIOException { + return (ChannelSftp) cs.acquireChannel("sftp", waitIfNoAvailable); // NOI18N + } + + @Override + public String toString() { + return name; + } + + public 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,156 @@ +/* + * 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.io.IOException; +import java.net.InetSocketAddress; +import java.net.NoRouteToHostException; +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.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.ExecutionLogger.Ref; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.ConnectorImplementation; +import org.openide.util.RequestProcessor; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.cnd.execution.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, IOException { + 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 NoRouteToHostException(uri.toString()); + } + + 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,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.impl.jsch; + +import java.io.File; +import java.net.URI; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.cnd.execution.spi.AuthDataProvider.class, path = "ConnectionService/ssh", position = 10) +public final class JSchDefaultAuthDataProvider implements AuthDataProvider { + + @Override + 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(); + if (userinfo == null) { + return null; + } + int pos = userinfo.indexOf(':'); + return clazz.cast(pos < 0 ? userinfo : userinfo.substring(0, pos)); + } + + if (String.class.equals(clazz) && AuthDataProvider.HOST.equals(name)) { + return clazz.cast(uri.getHost()); + } + + if (Integer.class.equals(clazz) && AuthDataProvider.PORT.equals(name)) { + int port = uri.getPort(); + if (port <= 0) { + port = 22; + } + return clazz.cast(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 clazz.cast(userinfo.substring(pos + 1).toCharArray()); + } + } + } + + if (String.class.equals(clazz) && "KnownHostsFile".equals(name)) { // NOI18N + File dir = new File(System.getProperty("user.home"), ".ssh"); // NOI18N + File file = new File(dir, "known_hosts"); // NOI18N + if (file.canRead()) { + return clazz.cast(file.getAbsolutePath()); // NOI18N + } + } + + if (String.class.equals(clazz) && "AuthType".equals(name)) { // NOI18N + return clazz.cast("SSHKey"); // NOI18N + } + + if (String.class.equals(clazz) && "SSHKeyFile".equals(name)) { // NOI18N + File dir = new File(System.getProperty("user.home"), ".ssh"); // NOI18N + File file = new File(dir, "id_dsa"); // NOI18N + if (file.canRead()) { + return clazz.cast(file.getAbsolutePath()); // NOI18N + } + file = new File(dir, "id_rsa"); // NOI18N + if (file.canRead()) { + return clazz.cast(file.getAbsolutePath()); // NOI18N + } + } + + return null; + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchFileOperationsSupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchFileOperationsSupport.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchFileOperationsSupport.java @@ -0,0 +1,129 @@ +/* + * 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.ChannelSftp; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.SftpATTRS; +import com.jcraft.jsch.SftpException; +import java.io.File; +import java.io.IOException; +import java.util.logging.Level; +import org.netbeans.modules.cnd.execution.ExecutionLogger; +import org.netbeans.modules.nativeexecution.impl.util.FileOperationsSupport; +import org.openide.util.Exceptions; + +/** + * + * @author Andrew + */ +public class JSchFileOperationsSupport implements FileOperationsSupport { + + private static final ExecutionLogger log = ExecutionLogger.getInstance(); + private final JSchConnection connection; + + public JSchFileOperationsSupport(JSchConnection connection) { + this.connection = connection; + } + + @Override + public String uploadFile(File localFile, String remoteFile, int mode) throws IOException { + ChannelSftp channel = null; + long remoteSize = -1; + long localSize = localFile.length(); + + try { + channel = connection.getChannelSftp(true); + + if (channel == null) { + return null; + } + + channel.connect(); + + try { + SftpATTRS rstat = channel.stat(remoteFile); + remoteSize = rstat.getSize(); + } catch (SftpException ex) { + // No such file ... + } + + if (remoteSize >= 0 && localSize != remoteSize) { + // Remote file exists, but it has different size + // Remove it first (otherwise channel.put() will + // fail if this file is opened for reading. + // (Any better idea?) + channel.rm(remoteFile); + remoteSize = -1; + } + + if (remoteSize < 0) { + String dir = new File(remoteFile).getParent(); + channel.mkdir(dir); + channel.put(localFile.getAbsolutePath(), remoteFile); + channel.chmod(mode, remoteFile); + } + + return remoteFile; + } catch (Exception ex) { + log.log(Level.WARNING, "Failed to upload {0}", localFile.getName()); // NOI18N + + if (remoteSize >= 0) { + log.log(Level.WARNING, "File {0} exists, but cannot be updated. Used by other process?", remoteFile); // NOI18N + } else { + log.log(Level.WARNING, "File {0} doesn't exist, and cannot be uploaded. Do you have enough privileges?", remoteFile); // NOI18N + } + + log.log(Level.WARNING, "You could try to use -J-Dcnd.tmpbase= to re-define default one."); // NOI18N + Exceptions.printStackTrace(ex); + throw new IOException(ex); + } finally { + if (channel != null) { + try { + connection.releaseChannel(channel); + } catch (JSchException ex) { + Exceptions.printStackTrace(ex); + } + } + } + } +} 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,77 @@ +/* + * 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 org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.cnd.execution.spi.NativeProcessImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.openide.util.Lookup; + +/** + * + * @author akrasny + */ +public final class JSchProcessCreator implements NativeProcessCreator { + + private final JSchConnection connectionImpl; + + public JSchProcessCreator(JSchConnection impl) { + this.connectionImpl = impl; + } + + @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 = new RemoteProcessImpl(connectionImpl, params); + return processImpl.createAndStart(); + } + + @Override + public Lookup getLookup() { + return Lookup.EMPTY; + } + +} \ No newline at end of file diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchSupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchSupport.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/JSchSupport.java @@ -0,0 +1,265 @@ +/* + * 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 2010 Sun Microsystems, Inc. + */ +package org.netbeans.modules.nativeexecution.impl.jsch; + +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.JSchException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.logging.Logger; +import org.netbeans.modules.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; + +/** + * + * @author ak119685 + */ +public final class JSchSupport { + + private static final int JSCH_CONNECTION_TIMEOUT = Integer.getInteger("jsch.connection.timeout", 10000); // NOI18N + private final static Logger log = ExecutionLogger.getInstance(); + + private JSchSupport() { + } + + /** + * Starts the specified command (executable + params) within the specified + * jsch connection. + * + * @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. + * @throws IOException - if unable to aquire an execution channel + * @throws JSchException - if JSch exception occurred + * @throws InterruptedException - if the thread was interrupted + */ + public static ChannelStreams startCommand(final JSchConnection connection, final String command, final ChannelParams params) + throws IOException { + + JSchWorker worker = new JSchWorker() { + + @Override + public ChannelStreams call() throws IOException { + ChannelExec echannel = connection.getChannelExec(true); + + + if (echannel == null) { + throw new NativeExecutionException("Cannot open exec channel on " + connection.toString() + " for " + command); // NOI18N + } + + echannel.setCommand(command); + echannel.setXForwarding(params == null ? false : params.x11forward); + try { + InputStream is = echannel.getInputStream(); + InputStream es = echannel.getErrStream(); + OutputStream os = new ProtectedOutputStream(echannel, echannel.getOutputStream()); + echannel.connect(JSCH_CONNECTION_TIMEOUT); + return new ChannelStreams(echannel, is, es, os); + } catch (JSchException ex) { + throw new NativeExecutionException(ex); + } catch (IOException ex) { + throw new NativeExecutionException(ex); + } + } + + @Override + public String toString() { + return command; + } + }; + + 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); +// InputStream is = shell.getInputStream(); +// InputStream es = new ByteArrayInputStream(new byte[0]); +// OutputStream os = shell.getOutputStream(); +// shell.connect(JSCH_CONNECTION_TIMEOUT); +// return new ChannelStreams(shell, is, es, os); +// } +// +// @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 IOException { + int retry = attempts; + + while (retry-- > 0) { + return worker.call(); + } +// +// 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 +// } +// } +// +// throw new IOException("Failed to execute " + worker.toString()); // NOI18N + throw new NativeExecutionException("Failed to execute " + worker.toString()); // NOI18N + } + + public final static class ChannelStreams { + + public final InputStream out; + public final InputStream err; + public final OutputStream in; + public final Channel channel; + + public ChannelStreams(Channel channel, InputStream out, + InputStream err, OutputStream in) { + this.channel = channel; + this.out = out; + this.err = err; + this.in = in; + } + } + + public static final class ChannelParams { + + private boolean x11forward = false; + + public void setX11Forwarding(boolean forward) { + this.x11forward = forward; + } + } + + private static interface JSchWorker { + + T call() throws IOException; + } + + private static class ProtectedOutputStream extends OutputStream { + + private final ChannelExec channel; + private final OutputStream stream; + + private ProtectedOutputStream(ChannelExec channel, OutputStream stream) { + this.stream = stream; + this.channel = channel; + } + + @Override + public void write(int b) throws IOException { + checkAlive(); + stream.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + checkAlive(); + stream.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + checkAlive(); + stream.write(b, off, len); + } + + @Override + public void flush() throws IOException { + checkAlive(); + stream.flush(); + } + + @Override + public void close() throws IOException { + if (!channel.isConnected()) { + return; + } + stream.close(); + } + + private void checkAlive() throws IOException { + if (!channel.isConnected()) { + throw new IOException("Channel is already closed"); // NOI18N + } + } + } +} 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,196 @@ +/* + * 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.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.ExecutionLogger.Ref; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +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.cnd.execution.spi.NativeProcessParams; +import org.netbeans.modules.cnd.execution.common.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 = Charset.forName("UTF-8"); //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()); + + if (getParams().isRedirectError()) { + write(writer, "exec 2>&1"); // NOI18N + } + + // 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.SIGTERM.name()); +// } catch (Exception ex) { +// // Exceptions.printStackTrace(ex); +// } + channel.disconnect(); + } + + @Override + public String toString() { + return displayName; + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteUserInfo.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteUserInfo.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/jsch/RemoteUserInfo.java @@ -0,0 +1,153 @@ +/* + * 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.impl.jsch; + +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; +import java.net.URI; +import java.util.Arrays; +import javax.security.auth.callback.PasswordCallback; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.cnd.execution.spi.UserInteraction; +import org.openide.util.NbBundle; + +final public class RemoteUserInfo implements UserInfo, UIKeyboardInteractive { + + private final AuthDataProvider dataProvider; + private final URI uri; + private final UserInteraction ui; + private PasswordCallback callback; + + public RemoteUserInfo(URI uri, AuthDataProvider dataProvider, UserInteraction ui) { + this.uri = uri; + this.dataProvider = dataProvider; + this.ui = ui; + } + + @Override + public String getPassphrase() { + 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() { + if (callback == null || callback.getPassword() == null) { + return null; + } + + 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) { + 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; + } + + return false; + } + + @Override + public boolean promptPassphrase(String message) { + callback = new PasswordCallback(message, false); + char[] pwd = dataProvider.getProperty(uri, char[].class, AuthDataProvider.PASSWORD, "passphrase", message); // NOI18N + if (pwd != null) { + callback.setPassword(pwd); + return true; + } + + return false; + } + + @Override + public boolean promptYesNo(String message) { + return (ui == null) ? false : ui.promptYesNo(uri, message); + } + + @Override + public void showMessage(String message) { + if (ui != null) { + ui.notify(uri, message); + } + } + + @Override + public String[] promptKeyboardInteractive(String destination, + String name, + String instruction, + String[] prompt, + boolean[] echo) { + + if (prompt.length == 1 && !echo[0]) { + // this is a password request + if (!promptPassword(NbBundle.getMessage(RemoteUserInfo.class, "MSG_PasswordInteractive", // NOI18N + destination, prompt[0]))) { + return null; + } else { + return new String[]{getPassword()}; + } + } else { + // AK: + // What else it could ask about? + // There was a code here that constructed dialog with all prompts + // based on promt / echo arrays. + // As I don't know usecases for it, I removed it ;) + + return null; + } + } +} 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,120 @@ +/* + * 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.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.common.ConnectionsInfo; +import org.netbeans.modules.nativeexecution.impl.common.ShellBasedConnectionInfoProvider; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.nativeexecution.impl.nbstart.NbStartConnectionInfoProvider; +import org.netbeans.modules.nativeexecution.impl.nbstart.NbStartProcessCreator; +import org.netbeans.modules.nativeexecution.impl.nbstart.NbStartUtility; +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() { + 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; + } + lookup = Lookups.fixed(LocalFileOperationsSupport.getInstance(), + new ShellBasedConnectionInfoProvider(), + new NbStartConnectionInfoProvider()); + } + + static LocalConnection getInstance() { + return instance; + } + + @Override + public boolean isConnected() { + return true; + } + + @Override + public NativeProcessCreator newProcessBuilderImpl() throws NativeExecutionException { + if (NbStartUtility.isSupported(ConnectionsInfo.getConnectionInfo(uri))) { + return new NbStartProcessCreator(); + } else { + 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.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import org.netbeans.modules.nativeexecution.impl.jsch.JSchConnector; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.ConnectorImplementation; +import org.netbeans.modules.cnd.execution.util.URIMatcher; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.cnd.execution.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, IOException { + 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/LocalFileOperationsSupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalFileOperationsSupport.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/LocalFileOperationsSupport.java @@ -0,0 +1,93 @@ +/* + * 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.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import org.netbeans.modules.nativeexecution.impl.util.FileOperationsSupport; + +public final class LocalFileOperationsSupport implements FileOperationsSupport { + + private static final LocalFileOperationsSupport instance = new LocalFileOperationsSupport(); + + private LocalFileOperationsSupport() { + } + + public static LocalFileOperationsSupport getInstance() { + return instance; + } + + @Override + public String uploadFile(File srcLocalFile, String dstRemoteFile, int mode) throws IOException { + copyFile(srcLocalFile, new File(dstRemoteFile)); + return dstRemoteFile; + } + + private static void copyFile(final File srcFile, final File dstFile) throws IOException { + if (dstFile.exists()) { + dstFile.delete(); + } + + dstFile.getParentFile().mkdirs(); + dstFile.createNewFile(); + + FileChannel source = null; + FileChannel destination = null; + + try { + source = new FileInputStream(srcFile).getChannel(); + destination = new FileOutputStream(dstFile).getChannel(); + destination.transferFrom(source, 0, source.size()); + dstFile.setExecutable(true); + } finally { + if (source != null) { + source.close(); + } + if (destination != null) { + destination.close(); + } + } + } +} 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 org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.cnd.execution.spi.NativeProcessImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.openide.util.Lookup; + +/** + * + * @author akrasny + */ +final class LocalProcessCreator implements NativeProcessCreator { + + @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(); + } + + @Override + public Lookup getLookup() { + return Lookup.EMPTY; + } +} \ 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.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.nativeexecution.impl.common.AbstractNativeProcessImpl; +import org.netbeans.modules.cnd.execution.spi.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/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.cnd.execution.spi.URIIdentifier; +import org.openide.util.lookup.ServiceProvider; + +/** + * + * @author akrasny + */ +@ServiceProvider(service = org.netbeans.modules.cnd.execution.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/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/Win32APISupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/Win32APISupport.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/local/Win32APISupport.java @@ -0,0 +1,97 @@ +/* + * 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.impl.local; + +import com.sun.jna.FromNativeContext; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; +import com.sun.jna.win32.StdCallLibrary; +import com.sun.jna.win32.W32APIFunctionMapper; +import com.sun.jna.win32.W32APITypeMapper; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +/* https://jna.dev.java.net/ */ +/* http://golesny.de/wiki/code:javahowtogetpid */ + +public interface Win32APISupport extends StdCallLibrary { + + final Map DEFAULT_OPTIONS = Collections.unmodifiableMap(new HashMap() { + + { + put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE); + put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE); + } + }); + final Win32APISupport instance = (Win32APISupport) Native.loadLibrary("kernel32", Win32APISupport.class, DEFAULT_OPTIONS); // NOI18N + + /* http://msdn.microsoft.com/en-us/library/ms683179(VS.85).aspx */ + HANDLE GetCurrentProcess(); + + /* http://msdn.microsoft.com/en-us/library/ms683215.aspx */ + int GetProcessId(HANDLE process); + + public class HANDLE extends PointerType { + + @Override + public Object fromNative(Object nativeValue, FromNativeContext context) { + Object o = super.fromNative(nativeValue, context); + if (InvalidHandle.equals(o)) { + return InvalidHandle; + } + return o; + } + } + public final static HANDLE InvalidHandle = new HANDLE() { + + { + super.setPointer(Pointer.createConstant(-1)); + } + + @Override + public void setPointer(Pointer p) { + throw new UnsupportedOperationException(); + } + }; +} + diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartConnectionInfoProvider.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartConnectionInfoProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartConnectionInfoProvider.java @@ -0,0 +1,72 @@ +/* + * 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.nbstart; + +import java.util.Map; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfoProvider; + +/** + * + * @author akrasny + */ +public final class NbStartConnectionInfoProvider implements ConnectionInfoProvider { + + private final static String NBENV_FILE = "nbenvfile"; // NOI18N + + @Override + public ConnectionInfo getConnectionInfo(Connection connection) throws NativeExecutionException { + return null; + } + + // methods for easy access... + static String getEnvironmentFile(NativeProcessParams params) { + Map info = params.getConnectionInfo(); + if (info == null || !info.containsKey(NBENV_FILE)) { + return null; + } + return info.get(NBENV_FILE); + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartLocalProcessImpl.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartLocalProcessImpl.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartLocalProcessImpl.java @@ -0,0 +1,75 @@ +/* + * 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.nbstart; + +import java.io.IOException; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; + +/** + * + * @author akrasny + */ +public final class NbStartLocalProcessImpl extends NbStartProcessImpl { + + private Process process = null; + + public NbStartLocalProcessImpl(final NativeProcessParams params) { + super(params); + } + + @Override + protected IOStreams createProcessImpl() throws IOException { + ProcessBuilder pb = new ProcessBuilder(getCommand()); + process = pb.start(); + return new IOStreams(process.getOutputStream(), process.getInputStream(), process.getErrorStream()); + } + + @Override + public void destroy() { + process.destroy(); + } + + @Override + protected int waitResultImpl() throws InterruptedException { + return process.waitFor(); + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartProcessCreator.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartProcessCreator.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartProcessCreator.java @@ -0,0 +1,88 @@ +/* + * 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.nbstart; + +import java.io.IOException; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.spi.NativeProcessCreator; +import org.netbeans.modules.cnd.execution.spi.NativeProcessImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author akrasny + */ +public final class NbStartProcessCreator implements NativeProcessCreator { + + private final TerminalConfigurationImpl config = new TerminalConfigurationImpl(); + + @Override + public NativeProcessImplementation createAndStart(final NativeProcessParams params) throws NativeExecutionException { + Connection connection = params.getConnection(); + + if (!NbStartUtility.isSupported(connection)) { + return null; + } + + NbStartProcessImpl processImpl = connection.isLocal() + ? new NbStartLocalProcessImpl(params) + : new NbStartRemoteProcessImpl(params); + + try { + processImpl.createAndStart(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + throw new NativeExecutionException(ex); + } + + return processImpl; + } + + @Override + public Lookup getLookup() { + return Lookups.fixed(config); + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartProcessImpl.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartProcessImpl.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartProcessImpl.java @@ -0,0 +1,306 @@ +/* + * 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.nbstart; + +import java.awt.Dimension; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.netbeans.api.extexecution.ProcessBuilder; +import org.netbeans.modules.cnd.execution.ExecutionLogger; +import org.netbeans.modules.cnd.execution.spi.NativeProcessImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.netbeans.modules.cnd.execution.util.HostInfo; +import org.netbeans.modules.dlight.api.terminal.ProcessTerminal; +import org.netbeans.modules.dlight.spi.terminal.TerminalConfiguration; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; + +/** + * + * @author akrasny + */ +abstract class NbStartProcessImpl implements NativeProcessImplementation, Lookup.Provider { + + private static final RequestProcessor rp = new RequestProcessor("NbStartProcessImplResize", 1); // NOI18N + private static final ExecutionLogger log = ExecutionLogger.getInstance(); + private final ConcurrentHashMap processInfo = new ConcurrentHashMap(); + protected final NativeProcessParams params; + private int pid; + private IOStreams streams; + private final InstanceContent ic = new InstanceContent(); + private final Lookup lookup = new AbstractLookup(ic); + + NbStartProcessImpl(NativeProcessParams params) { + this.params = params; + } + + protected List getCommand() throws IOException { + HostInfo hostInfo = HostInfo.getFor(params.getConnection()); + List command = new ArrayList(); + + command.add(NbStartUtility.getInstance().getPath(params.getConnection())); + + String wdir = params.getWorkingDirectory(); + if (wdir != null && !wdir.isEmpty()) { + command.add("--dir"); // NOI18N + command.add(wdir); + } + + boolean isTerminalMode = false; + String terminalEmulation = null; + + TerminalConfiguration termConfig = params.getLookup().lookup(TerminalConfiguration.class); + if (termConfig != null && termConfig.isTerminalMode()) { + isTerminalMode = true; + terminalEmulation = termConfig.getEmulation(); + } + + if (!isTerminalMode) { + command.add("--no-pty"); // NOI18N + } else { +// Pty pty = info.getPty(); +// if (pty != null) { +// command.add("-p"); // NOI18N +// command.add(pty.getSlaveName()); +// } + } + +// if (!isPtyMode && info.isUnbuffer()) { +// try { +// UnbufferSupport.initUnbuffer(info.getExecutionEnvironment(), info.getEnvironment()); +// } catch (IOException ex) { +// Logger.getInstance().log(Level.FINE, "initUnbuffer failed", ex); // NOI18N +// } +// } + +// if (info.getInitialSuspend()) { +// command.add("-w"); // NOI18N +// } + + boolean getStatus = false; // NbStartProcessParams.isStatusEx(params); + if (getStatus) { + command.add("--report"); // NOI18N + command.add(hostInfo.getTempDir() + "/status"); // NOI18N + } + + String envFile = NbStartConnectionInfoProvider.getEnvironmentFile(params); + if (envFile != null) { + command.add("--readenv"); // NOI18N + command.add(envFile); + } + + Map env = params.getEnvironment(); + + if (env != null) { +// Map userDefinedMap = userEnv.getUserDefinedMap(); + + for (Map.Entry entry : env.entrySet()) { +// if (isWindows() && entry.getKey().equalsIgnoreCase("PATH")) { // NOI18N +// command.add("--env"); // NOI18N +// command.add(entry.getKey() + "=" + WindowsSupport.getInstance().convertToAllShellPaths(entry.getValue())); // NOI18N +// continue; +// } + command.add("--env"); // NOI18N + command.add(entry.getKey() + "=" + entry.getValue()); // NOI18N + } + } + + if (terminalEmulation != null) { + command.add("--env"); // NOI18N + command.add("TERM=" + terminalEmulation); // NOI18N + } + + if (params.isRedirectError()) { + command.add("--redirect-error"); // NOI18N + } + + final String shellScript = params.getShellScript(); + if (shellScript != null) { + command.add(params.getShell()); + command.add("-c"); // NOI18N + command.add("exec " + params.getShellScript()); // NOI18N + } else { + command.addAll(params.getCommand()); + } + return command; + } + + @Override + public int getPID() { + return pid; + } + + public final void createAndStart() throws IOException { + streams = createProcessImpl(); + readProcessInfo(getInputStream()); + final String tty = getProcessInfo("TTY"); // NOI18N + + if (tty != null) { + ic.add(new ProcessTerminal() { + @Override + protected void ttyResized(Dimension size, Dimension pixelSize) { + try { + String pty = NbStartUtility.getInstance().getPath(params.getConnection()); + ProcessBuilder pb = params.getConnection().getProcessBuilder(); + pb.setExecutable(pty); + pb.setArguments(Arrays.asList("-p", tty, // NOI18N + "--resize", // NOI18N + size.width + "," + size.height + "," // NOI18N + + pixelSize.width + "," + pixelSize.height)); // NOI18N + pb.call(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + } + } + + @Override + public Lookup getLookup() { + return lookup; + } + +// @Override +// public T getCapability(Class clazz) { +// if (clazz.isAssignableFrom(TerminalCapability.class)) { +// return clazz.cast(terminalCapability); +// } +// +// return clazz.cast(null); +// } + protected abstract int waitResultImpl() throws InterruptedException; + + @Override + public final int waitResult() throws InterruptedException { + return waitResultImpl(); + } + + @Override + public final InputStream getErrorStream() { + return streams.err; + } + + @Override + public final InputStream getInputStream() { + return streams.out; + } + + @Override + public final OutputStream getOutputStream() { + return streams.in; + } + + protected abstract IOStreams createProcessImpl() throws IOException; + + private void readProcessInfo(InputStream fromProcessStream) throws IOException { + String line; + + while (!(line = readLine(fromProcessStream).trim()).isEmpty()) { + addProcessInfo(line); + } + + String pidProperty = getProcessInfo("PID"); // NOI18N + + if (pidProperty == null) { + InputStream error = getErrorStream(); + while (!(line = readLine(error).trim()).isEmpty()) { + log.fine(line); + } + throw new InternalError("Failed to get process PID"); // NOI18N + } + + pid = Integer.parseInt(pidProperty); + } + + private String readLine(final InputStream is) throws IOException { + int c; + StringBuilder sb = new StringBuilder(20); + + // while (!isInterrupted()) { + while (true) { + c = is.read(); + + if (c < 0 || c == '\n') { + break; + } + + sb.append((char) c); + } + + return sb.toString().trim(); + } + + protected void addProcessInfo(String info) { + int spos = info.indexOf('='); + if (spos < 0) { + throw new IllegalArgumentException("info must be in format NAME=VALUE - was " + info); // NOI18N + } + processInfo.put(info.substring(0, spos), info.substring(spos + 1)); + } + + protected String getProcessInfo(final String string) { + return processInfo.get(string); + } + + protected final class IOStreams { + + private final OutputStream in; + private final InputStream out; + private final InputStream err; + + public IOStreams(OutputStream in, InputStream out, InputStream err) { + this.in = in; + this.out = out; + this.err = err; + } + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartRemoteProcessImpl.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartRemoteProcessImpl.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartRemoteProcessImpl.java @@ -0,0 +1,128 @@ +/* + * 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.nbstart; + +import com.jcraft.jsch.JSchException; +import java.io.IOException; +import org.netbeans.modules.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.common.SignalUtils; +import org.netbeans.modules.cnd.execution.spi.ConnectionImplementation; +import org.netbeans.modules.cnd.execution.spi.NativeProcessParams; +import org.netbeans.modules.nativeexecution.impl.jsch.JSchConnection; +import org.netbeans.modules.nativeexecution.impl.jsch.JSchSupport; +import org.netbeans.modules.nativeexecution.impl.jsch.JSchSupport.ChannelStreams; +import org.openide.util.Exceptions; + +/** + * + * @author akrasny + */ +public final class NbStartRemoteProcessImpl extends NbStartProcessImpl { + + private JSchConnection jschConnection; + private ChannelStreams jschStreams; + + public NbStartRemoteProcessImpl(final NativeProcessParams params) { + super(params); + } + + @Override + protected IOStreams createProcessImpl() throws IOException { + JSchSupport.ChannelParams chParams = new JSchSupport.ChannelParams(); + chParams.setX11Forwarding(false /*NbStartProcessParams.isX11Forwarding(params)*/); + + StringBuilder sb = new StringBuilder(); + + for (String arg : getCommand()) { + sb.append('\'').append(arg).append('\'').append(' '); // NOI18N + } + + Connection connection = params.getConnection(); + ConnectionImplementation cimpl = ConnectionAccessor.getDefault().getImpl(connection); + jschConnection = (JSchConnection) cimpl; + + try { + jschStreams = JSchSupport.startCommand(jschConnection, sb.toString(), chParams); + } catch (NativeExecutionException ex) { + Exceptions.printStackTrace(ex); + throw new IOException(ex); + } + return new IOStreams(jschStreams.in, jschStreams.out, jschStreams.err); + } + + @Override + protected int waitResultImpl() throws InterruptedException { + if (jschStreams == null || jschStreams.channel == null) { + return -1; + } + + try { + while (jschStreams.channel.isConnected()) { + Thread.sleep(200); + } + +// finishing(); + + return jschStreams.channel.getExitStatus(); + } finally { + if (jschStreams != null) { + try { + jschConnection.releaseChannel(jschStreams.channel); + } catch (JSchException ex) { + Exceptions.printStackTrace(ex); + } + } + } + } + + @Override + public void destroy() { +// try { +// jschStreams.channel.sendSignal(SignalUtils.Signal.SIGTERM.name()); +// } catch (Exception ex) { +// // Exceptions.printStackTrace(ex); +// } + jschStreams.channel.disconnect(); + } +} diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartUtility.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartUtility.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/NbStartUtility.java @@ -0,0 +1,108 @@ +/* + * 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.nbstart; + +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.spi.support.ConnectionInfo; +import org.netbeans.modules.cnd.execution.util.HostInfo; +import org.netbeans.modules.nativeexecution.impl.common.HelperUtility; + +/** + * + * @author Andrew + */ +public class NbStartUtility extends HelperUtility { + + private static final boolean ENABLED = Boolean.parseBoolean(System.getProperty("enable.nbstart", "true")); // NOI18N + private final static NbStartUtility instance = new NbStartUtility(); + + public NbStartUtility() { + super("bin/nativeexecution/${osname}-${platform}${_isa}/pty"); // NOI18N + } + + public static NbStartUtility getInstance() { + return instance; + } + + public static boolean isSupported(Connection connection) { + return instance.isSupported(HostInfo.getFor(connection)); + } + + public static boolean isSupported(final ConnectionInfo info) { + if (!ENABLED) { + return false; + } + if (info == null) { + return false; + } + return true; + } + + @Override + public boolean isSupported(HostInfo hostInfo) { + if (!ENABLED) { + return false; + } + + try { + switch (hostInfo.getOS().getFamily()) { + case MACOSX: + case SOLARIS: + case LINUX: + return true; + case WINDOWS: + // For now will disable it on Windows, as there are some + // side-effects with paths (need deeper studying) +// Shell activeShell = WindowsSupport.getInstance().getActiveShell(); +// if (activeShell == null || !Shell.ShellType.CYGWIN.equals(activeShell.type)) { +// return false; +// } +// return getPath(executionEnvironment) != null; + return false; + default: + return false; + } + } catch (Exception ex) { + return false; + } + } +} \ No newline at end of file diff --git a/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/TerminalConfigurationImpl.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/TerminalConfigurationImpl.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/nbstart/TerminalConfigurationImpl.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.impl.nbstart; + +import org.netbeans.modules.dlight.spi.terminal.TerminalConfiguration; + +/** + * + * @author Andrew + */ +public final class TerminalConfigurationImpl implements TerminalConfiguration { + + private boolean terminalMode; + + private String emulation; + + public TerminalConfigurationImpl() { + super(); + } + + public boolean isTerminalMode() { + return terminalMode; + } + + public void setTerminalMode(boolean terminalMode) { + this.terminalMode = terminalMode; + } + + public String getEmulation() { + return emulation; + } + + public void setEmulation(String emulation) { + this.emulation = emulation; + } + +} 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.cnd.execution.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/src/org/netbeans/modules/nativeexecution/impl/util/FileOperationsSupport.java b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/util/FileOperationsSupport.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/src/org/netbeans/modules/nativeexecution/impl/util/FileOperationsSupport.java @@ -0,0 +1,54 @@ +/* + * 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.File; +import java.io.IOException; + +/** + * + * @author Andrew + */ +public interface FileOperationsSupport { + + public String uploadFile(File srcLocalFile, String dstRemoteFile, int mode) throws IOException; +} diff --git a/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/ExecutionTestCase.java b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/ExecutionTestCase.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/ExecutionTestCase.java @@ -0,0 +1,60 @@ +/* + * 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 org.netbeans.junit.NbTestCase; + +/** + * + * @author Andrew + */ +public abstract class ExecutionTestCase extends NbTestCase { + + static { + // Setting netbeans.dirs makes installedFileLocator work properly + System.setProperty("netbeans.dirs", NbClustersInfoProvider.getClusters()); + } + + public ExecutionTestCase(String name) { + super(name); + } +} diff --git a/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/NbClustersInfoProvider.java b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/NbClustersInfoProvider.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/NbClustersInfoProvider.java @@ -0,0 +1,121 @@ +/* + * 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.io.File; +import java.io.FileFilter; +import java.util.TreeSet; +import junit.framework.Assert; +import org.netbeans.junit.NbModuleSuite; + +/** + * + * @author akrasny + */ +public final class NbClustersInfoProvider { + + private NbClustersInfoProvider() { + } + + public static String getClusters() { + // Setting netbeans.dirs makes installedFileLocator work properly + File[] clusters = findClusters(); + StringBuilder sb = new StringBuilder(); + for (File cluster : clusters) { + if (sb.length() > 0) { + sb.append(File.pathSeparator); + } + sb.append(cluster.getPath()); + } + return sb.toString(); + } + + // it's like what org.netbeans.junit.NbModuleSuite does, + // but reusing NbModuleSuite will cause too massive changes in existing CND tests + private static File[] findClusters() { + File netbeans = findNetbeans(); + assert netbeans != null; + File[] clusters = netbeans.listFiles(new FileFilter() { + + @Override + public boolean accept(File dir) { + if (dir.isDirectory()) { + File m = new File(new File(dir, "config"), "Modules"); + return m.exists(); + } + return false; + } + }); + return clusters; + } + + // it's like what org.netbeans.junit.NbModuleSuite does, + // but reusing NbModuleSuite will cause too massive changes in existing CND tests + private static File findNetbeans() { + try { + Class lookup = Class.forName("org.openide.util.Lookup"); // NOI18N + File util = new File(lookup.getProtectionDomain().getCodeSource().getLocation().toURI()); + Assert.assertTrue("Util exists: " + util, util.exists()); + return util.getParentFile().getParentFile().getParentFile(); + } catch (Exception ex) { + try { + File nbjunit = new File(NbModuleSuite.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + File harness = nbjunit.getParentFile().getParentFile(); + Assert.assertEquals("NbJUnit is in harness", "harness", harness.getName()); + TreeSet sorted = new TreeSet(); + File[] listFiles = harness.getParentFile().listFiles(); + if (listFiles != null) { + for (File p : listFiles) { + if (p.getName().startsWith("platform")) { + sorted.add(p); + } + } + } + Assert.assertFalse("Platform shall be found in " + harness.getParent(), sorted.isEmpty()); + return sorted.last(); + } catch (Exception ex2) { + Assert.fail("Cannot find utilities JAR: " + ex + " and: " + ex2); + } + return null; + } + } +} diff --git a/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/common/HelperUtilityTest.java b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/common/HelperUtilityTest.java new file mode 100644 --- /dev/null +++ b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/common/HelperUtilityTest.java @@ -0,0 +1,103 @@ +/* + * 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.net.URI; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.nativeexecution.impl.ExecutionTestCase; +import org.netbeans.modules.cnd.execution.util.HostInfo; + +/** + * + * @author Andrew + */ +public class HelperUtilityTest extends ExecutionTestCase { + + public HelperUtilityTest(String name) { + super(name); + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + } + + @After + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Test of getPath method, of class HelperUtility. + */ + @Test + public void testGetPath() throws Exception { + HelperUtility test = new HelperUtility("bin/nativeexecution/$osname-${platform}$_isa/pty_open"); + Connection localConnection = ConnectionManager.getLocalConnection(); + HostInfo info = HostInfo.getFor(localConnection); + System.out.println(test.getLocalFileFor(info).getAbsolutePath()); + System.out.println(test.getPath(localConnection)); + String rurl = System.getProperty("test.remote.url"); + if (rurl != null) { + Connection remote = ConnectionManager.connect(new URI(rurl)); + System.out.println(test.getPath(remote)); + } else { + System.out.println("test.remote.url property is not defined..."); + } + } +} diff --git a/dlight.nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/RedirectErrorTest.java b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/common/RedirectErrorTest.java copy from dlight.nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/RedirectErrorTest.java copy to nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/common/RedirectErrorTest.java --- a/dlight.nativeexecution/test/unit/src/org/netbeans/modules/nativeexecution/RedirectErrorTest.java +++ b/nativeexecution.impl/test/unit/src/org/netbeans/modules/nativeexecution/impl/common/RedirectErrorTest.java @@ -39,77 +39,63 @@ * * Portions Copyrighted 2012 Sun Microsystems, Inc. */ -package org.netbeans.modules.nativeexecution; +package org.netbeans.modules.nativeexecution.impl.common; -import junit.framework.Test; -import org.junit.After; -import org.junit.Before; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment; -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory; -import org.netbeans.modules.nativeexecution.api.NativeProcessBuilder; -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager; -import org.netbeans.modules.nativeexecution.api.util.ProcessUtils; -import org.netbeans.modules.nativeexecution.api.util.ProcessUtils.ExitStatus; -import org.netbeans.modules.nativeexecution.test.ForAllEnvironments; -import org.netbeans.modules.nativeexecution.test.NativeExecutionBaseTestCase; -import org.netbeans.modules.nativeexecution.test.NativeExecutionBaseTestSuite; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.ExecutionException; +import org.netbeans.api.extexecution.ExecutionService; +import org.netbeans.api.extexecution.input.LineProcessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.cnd.execution.api.NativeExecutionException; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.netbeans.modules.nativeexecution.impl.ExecutionTestCase; +import org.openide.util.Exceptions; /** * * @author akrasny */ -public final class RedirectErrorTest extends NativeExecutionBaseTestCase { +public final class RedirectErrorTest extends ExecutionTestCase { + + private URI remoteURI = null; public RedirectErrorTest(String name) { super(name); - } - public RedirectErrorTest(String name, ExecutionEnvironment execEnv) { - super(name, execEnv); - } - - public static Test suite() { - return new NativeExecutionBaseTestSuite(RedirectErrorTest.class); - } - - @Before - @Override - protected void setUp() throws Exception { - super.setUp(); - ExecutionEnvironment env = getTestExecutionEnvironment(); - if (env != null) { - ConnectionManager.getInstance().connect(env); + String testURI = System.getProperty("ne.remote.uri"); // NOI18N + if (testURI != null) { + try { + remoteURI = new URI(testURI); + } catch (URISyntaxException ex) { + Exceptions.printStackTrace(ex); + } } } - @After - @Override - protected void tearDown() throws Exception { - ExecutionEnvironment env = getTestExecutionEnvironment(); - if (env != null) { - ConnectionManager.getInstance().disconnect(env); + @org.junit.Test + public void testRedirectError_remote() throws Exception { + if (remoteURI != null) { + doTestRedirectError(ConnectionManager.connect(remoteURI, new AuthDataProviderImpl())); } - super.tearDown(); - } - - @org.junit.Test - @ForAllEnvironments(section = "remote.platforms") - public void testRedirectError_remote() throws Exception { - doTestRedirectError(getTestExecutionEnvironment()); } @org.junit.Test public void testRedirectError_local() throws Exception { - doTestRedirectError(ExecutionEnvironmentFactory.getLocal()); + doTestRedirectError(ConnectionManager.getLocalConnection()); } - private void doTestRedirectError(ExecutionEnvironment env, boolean cmdLine, boolean tty, boolean redirectError) { - if (env == null) { + private void doTestRedirectError(Connection connection, boolean cmdLine, boolean tty, boolean redirectError) throws NativeExecutionException { + if (connection == null || !connection.isConnected()) { return; } System.out.println(); - System.out.println("RedirectErrorTest @ " + env.getDisplayName()); // NOI18N + System.out.println("RedirectErrorTest @ " + connection.getDisplayName()); // NOI18N System.out.println("\tconfigure builder using " + (cmdLine ? "setCommandLine()" : "setExecutable()")); // NOI18N System.out.println("\tconfigure builder " + (redirectError ? "" : "not ") + "to do redirectError()"); // NOI18N System.out.println("\tconfigure builder " + (tty ? "" : "not ") + "to be started in a pseudo-terminal"); // NOI18N @@ -117,21 +103,22 @@ boolean errorRedirect = (tty || redirectError) ? true : false; System.out.println("Extected that error goes to " + (errorRedirect ? "output" : "error")); // NOI18N - NativeProcessBuilder npb = NativeProcessBuilder.newProcessBuilder(env); + NativeProcessBuilder npb = connection.newNativeProcessBuilder(); if (cmdLine) { - npb.setCommandLine("wrong"); // NOI18N + npb.setShellScript("/bin/sh", "wrong"); // NOI18N } else { - npb.setExecutable("wrong"); // NOI18N + npb.setCommand("wrong"); // NOI18N } - npb.setUsePty(tty); +// npb.setUsePty(tty); if (redirectError) { - npb.redirectError(); + npb.redirectErrorStream(true); } - ExitStatus status = ProcessUtils.execute(npb); + ExitStatus status = exec(npb); + System.out.println("Result is: "); // NOI18N System.out.println(status.toString()); @@ -142,14 +129,66 @@ } } - private void doTestRedirectError(ExecutionEnvironment env) { - doTestRedirectError(env, false, false, false); - doTestRedirectError(env, false, false, true); - doTestRedirectError(env, false, true, false); - doTestRedirectError(env, false, true, true); - doTestRedirectError(env, true, false, false); - doTestRedirectError(env, true, false, true); - doTestRedirectError(env, true, true, false); - doTestRedirectError(env, true, true, true); + private void doTestRedirectError(Connection connection) throws NativeExecutionException { + doTestRedirectError(connection, false, false, false); + doTestRedirectError(connection, false, false, true); +// doTestRedirectError(connection, false, true, false); +// doTestRedirectError(connection, false, true, true); + doTestRedirectError(connection, true, false, false); + doTestRedirectError(connection, true, false, true); +// doTestRedirectError(connection, true, true, false); +// doTestRedirectError(connection, true, true, true); + } + + private ExitStatus exec(NativeProcessBuilder npb) { + try { + Writer outWriter = new StringWriter(); + Writer errWriter = new StringWriter(); + ExecutionService execService = ExecutionService.newService(npb, outWriter, errWriter); + Integer rc = execService.run().get(); + return new ExitStatus(rc, outWriter.toString(), errWriter.toString()); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } catch (ExecutionException ex) { + Exceptions.printStackTrace(ex); + } + + return new ExitStatus(-1, "Internal Error: FAILED", "Internal Error: FAILED"); // NOI18N + } + + 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"; + } + + if (String.class.equals(clazz) && "AuthType".equals(name)) { // NOI18N + result = "password"; // NOI18N + } + + return clazz.cast(result); + } + } + + private static class ExitStatus { + + private final Integer rc; + private final String output; + private final String error; + + private ExitStatus(Integer rc, String out, String err) { + this.rc = rc; + this.output = out; + this.error = err; + } + + @Override + public String toString() { + return "ExitCode: " + rc + "; Output: " + output + "; Error: " + error; // NOI18N + } } } 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.io.StringWriter; +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.api.extexecution.ExecutionService; +import org.netbeans.modules.cnd.execution.access.ConnectionAccessor; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.cnd.execution.api.EnvironmentMap; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.modules.cnd.execution.spi.AuthDataProvider; +import org.openide.util.Exceptions; + +/** + * + * @author Andrew + */ +public class RemoteProcessImplTest { + + private URI remoteURI = null; + + public RemoteProcessImplTest() { + String testURI = System.getProperty("ne.remote.uri"); // NOI18N + if (testURI != null) { + try { + remoteURI = new URI(testURI); + } 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.newNativeProcessBuilder(); + 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.newNativeProcessBuilder().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"); + StringWriter output = new StringWriter(); + ExecutionService.newService(pb, output, null).run().get(); + String data = "\n" + output.toString() + "\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"; + } + + 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,149 @@ +/* + * 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.io.StringWriter; +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.api.extexecution.ExecutionService; +import org.netbeans.modules.cnd.execution.api.Connection; +import org.netbeans.modules.cnd.execution.api.ConnectionManager; +import org.netbeans.modules.cnd.execution.api.EnvironmentMap; +import org.netbeans.modules.cnd.execution.api.NativeProcessBuilder; +import org.netbeans.modules.nativeexecution.impl.ExecutionTestCase; + +/** + * + * @author akrasny + */ +public class LocalProcessTest extends ExecutionTestCase { + + public LocalProcessTest(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 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.newNativeProcessBuilder(); + 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.newNativeProcessBuilder().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"); + StringWriter output = new StringWriter(); + ExecutionService.newService(pb, output, null).run().get(); + String data = "\n" + output.toString() + "\n"; + System.out.println(data); + 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/nbbuild/cluster.properties b/nbbuild/cluster.properties --- a/nbbuild/cluster.properties +++ b/nbbuild/cluster.properties @@ -269,7 +269,6 @@ defaults,\ derby,\ diff,\ - dlight.nativeexecution,\ dlight.terminal,\ editor,\ editor.actions,\ @@ -991,10 +990,14 @@ nb.cluster.platform,\ nb.cluster.ide nb.cluster.dlight=\ + cnd.execution,\ dlight.kit,\ dlight.libs.common,\ + dlight.nativeexecution,\ dlight.remote,\ dlight.remote.impl,\ + execmerge,\ + nativeexecution.impl,\ remotefs.versioning