You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by pm...@apache.org on 2018/01/23 20:50:53 UTC
svn commit: r1822052 - in /jmeter/trunk: ./ bin/ src/core/org/apache/jmeter/
src/core/org/apache/jmeter/engine/ src/core/org/apache/jmeter/rmi/
src/core/org/apache/jmeter/samplers/ src/core/org/apache/jmeter/threads/
xdocs/
Author: pmouawad
Date: Tue Jan 23 20:50:52 2018
New Revision: 1822052
URL: http://svn.apache.org/viewvc?rev=1822052&view=rev
Log:
Bug 62039 - Distributed testing : Provide ability to use SSL
Bugzilla Id: 62039
Added:
jmeter/trunk/bin/create-rmi-keystore.bat (with props)
jmeter/trunk/bin/create-rmi-keystore.sh (with props)
jmeter/trunk/src/core/org/apache/jmeter/rmi/
jmeter/trunk/src/core/org/apache/jmeter/rmi/AliasKeyManager.java (with props)
jmeter/trunk/src/core/org/apache/jmeter/rmi/RMIServerSocketFactoryImpl.java (with props)
jmeter/trunk/src/core/org/apache/jmeter/rmi/RmiUtils.java (with props)
jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIClientSocketFactory.java (with props)
jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIServerSocketFactory.java (with props)
Modified:
jmeter/trunk/bin/jmeter.properties
jmeter/trunk/bin/user.properties
jmeter/trunk/build.xml
jmeter/trunk/src/core/org/apache/jmeter/JMeter.java
jmeter/trunk/src/core/org/apache/jmeter/engine/ClientJMeterEngine.java
jmeter/trunk/src/core/org/apache/jmeter/engine/RemoteJMeterEngineImpl.java
jmeter/trunk/src/core/org/apache/jmeter/samplers/RemoteSampleListenerImpl.java
jmeter/trunk/src/core/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java
jmeter/trunk/xdocs/changes.xml
Added: jmeter/trunk/bin/create-rmi-keystore.bat
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/create-rmi-keystore.bat?rev=1822052&view=auto
==============================================================================
--- jmeter/trunk/bin/create-rmi-keystore.bat (added)
+++ jmeter/trunk/bin/create-rmi-keystore.bat Tue Jan 23 20:50:52 2018
@@ -0,0 +1,40 @@
+@echo off
+
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+rem
+keytool -genkey -keyalg RSA -alias rmi -keystore rmi_keystore.jks -storepass changeit -validity 7 -keysize 2048
+
+echo "Copy the generated rmi_keystore.jks to jmeter/bin folder or reference it in property 'server.rmi.ssl.keystore.file'"
+@echo off
+
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed with
+rem this work for additional information regarding copyright ownership.
+rem The ASF licenses this file to You under the Apache License, Version 2.0
+rem (the "License"); you may not use this file except in compliance with
+rem the License. You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem Unless required by applicable law or agreed to in writing, software
+rem distributed under the License is distributed on an "AS IS" BASIS,
+rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem See the License for the specific language governing permissions and
+rem limitations under the License.
+rem
+keytool -genkey -keyalg RSA -alias rmi -keystore rmi_keystore.jks -storepass changeit -validity 7 -keysize 2048
+
+echo "Copy the generated rmi_keystore.jks to jmeter/bin folder or reference it in property 'server.rmi.ssl.keystore.file'"
\ No newline at end of file
Propchange: jmeter/trunk/bin/create-rmi-keystore.bat
------------------------------------------------------------------------------
svn:eol-style = native
Added: jmeter/trunk/bin/create-rmi-keystore.sh
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/create-rmi-keystore.sh?rev=1822052&view=auto
==============================================================================
--- jmeter/trunk/bin/create-rmi-keystore.sh (added)
+++ jmeter/trunk/bin/create-rmi-keystore.sh Tue Jan 23 20:50:52 2018
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+keytool -genkey -keyalg RSA -alias rmi -keystore rmi_keystore.jks -storepass changeit -validity 7 -keysize 2048
+
+echo "Copy the generated rmi_keystore.jks to jmeter/bin folder or reference it in property 'server.rmi.ssl.keystore.file'"
+#!/bin/sh
+
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements. See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+##
+keytool -genkey -keyalg RSA -alias rmi -keystore rmi_keystore.jks -storepass changeit -validity 7 -keysize 2048
+
+echo "Copy the generated rmi_keystore.jks to jmeter/bin folder or reference it in property 'server.rmi.ssl.keystore.file'"
\ No newline at end of file
Propchange: jmeter/trunk/bin/create-rmi-keystore.sh
------------------------------------------------------------------------------
svn:eol-style = native
Modified: jmeter/trunk/bin/jmeter.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/jmeter.properties?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/bin/jmeter.properties (original)
+++ jmeter/trunk/bin/jmeter.properties Tue Jan 23 20:50:52 2018
@@ -292,6 +292,39 @@ remote_hosts=127.0.0.1
# Define the following property to cause JMeter to exit after the first test
#server.exitaftertest=true
+
+#
+# Type of keystore : JKS
+#
+#server.rmi.ssl.keystore.type=JKS
+#
+# Keystore file that contains private key
+#
+#server.rmi.ssl.keystore.file=rmi_keystore.jks
+#
+# Password of Keystore
+#
+#server.rmi.ssl.keystore.password=changeit
+#
+# Key alias
+#
+#server.rmi.ssl.keystore.alias=rmi
+#
+# Type of truststore : JKS
+#
+#server.rmi.ssl.truststore.type=JKS
+#
+# Keystore file that contains certificate
+#
+#server.rmi.ssl.truststore.file=rmi_keystore.jks
+#
+# Password of Trust store
+#
+#server.rmi.ssl.truststore.password=changeit
+#
+# Set this if you don't want to use SSL for RMI
+#
+#server.rmi.ssl.disable=false
#---------------------------------------------------------------------------
# Include Controller
#---------------------------------------------------------------------------
Modified: jmeter/trunk/bin/user.properties
URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/user.properties?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/bin/user.properties (original)
+++ jmeter/trunk/bin/user.properties Tue Jan 23 20:50:52 2018
@@ -100,3 +100,39 @@
# This property is used by menu item "Export transactions for report"
# It is used to select which transactions by default will be exported
#jmeter.reportgenerator.exported_transactions_pattern=[a-zA-Z0-9_\\-{}\\$\\.]*[-_][0-9]*
+
+
+########################################################################
+################## DISTRIBUTED TESTING CONFIGURATION ##################
+########################################################################
+# Type of keystore : JKS
+#
+#server.rmi.ssl.keystore.type=JKS
+#
+# Keystore file that contains private key
+#
+#server.rmi.ssl.keystore.file=rmi_keystore.jks
+#
+# Password of Keystore
+#
+#server.rmi.ssl.keystore.password=changeit
+#
+# Key alias
+#
+#server.rmi.ssl.keystore.alias=rmi
+#
+# Type of truststore : JKS
+#
+#server.rmi.ssl.truststore.type=JKS
+#
+# Keystore file that contains certificate
+#
+#server.rmi.ssl.truststore.file=rmi_keystore.jks
+#
+# Password of Trust store
+#
+#server.rmi.ssl.truststore.password=changeit
+#
+# Set this if you don't want to use SSL for RMI
+#
+#server.rmi.ssl.disable=false
\ No newline at end of file
Modified: jmeter/trunk/build.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/build.xml?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/build.xml (original)
+++ jmeter/trunk/build.xml Tue Jan 23 20:50:52 2018
@@ -2577,10 +2577,11 @@ run JMeter unless all the JMeter jars ar
<property name="batchtestserver.out" location="${basedir}/bin"/>
<property name="batchtestserver.log" value="BatchTestServer.log"/>
<!-- rmi_force_localhost is used by the Travis build -->
+ <exec executable="hostname" outputproperty="computer.hostname"/>
<condition property="rmi_hostname_option" value="-Ddummy=true">
<not><isset property="rmi_force_localhost"/></not>
</condition>
- <condition property="rmi_hostname_option" value="-Djava.rmi.server.hostname=localhost">
+ <condition property="rmi_hostname_option" value="-Djava.rmi.server.hostname=${computer.hostname}">
<isset property="rmi_force_localhost"/>
</condition>
<condition property="jacoco_params" value="${jacocoagent}" else="-Djacoco_dummy=">
@@ -2622,7 +2623,7 @@ run JMeter unless all the JMeter jars ar
<!-- Increase as necessary to ensure server has time to start -->
<sleep seconds="2"/>
<antcall target="batchtest">
- <param name="remote" value="-Rlocalhost:${rmi_port}"/>
+ <param name="remote" value="-R${computer.hostname}:${rmi_port}"/>
<param name="taskname" value="client"/>
<!-- Default the test name so we can override with a parameter -->
</antcall>
Modified: jmeter/trunk/src/core/org/apache/jmeter/JMeter.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/JMeter.java?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/JMeter.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/JMeter.java Tue Jan 23 20:50:52 2018
@@ -78,6 +78,7 @@ import org.apache.jmeter.report.dashboar
import org.apache.jmeter.report.dashboard.ReportGenerator;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.reporters.Summariser;
+import org.apache.jmeter.rmi.RmiUtils;
import org.apache.jmeter.samplers.Remoteable;
import org.apache.jmeter.samplers.SampleEvent;
import org.apache.jmeter.save.SaveService;
@@ -496,7 +497,7 @@ public class JMeter implements JMeterPlu
} else if (parser.getArgumentById(SERVER_OPT) != null) {
// Start the server
try {
- RemoteJMeterEngineImpl.startServer(JMeterUtils.getPropDefault("server_port", 0)); // $NON-NLS-1$
+ RemoteJMeterEngineImpl.startServer(RmiUtils.getRmiRegistryPort()); // $NON-NLS-1$
startOptionalServers();
} catch (Exception ex) {
System.err.println("Server failed to start: "+ex);//NOSONAR
Modified: jmeter/trunk/src/core/org/apache/jmeter/engine/ClientJMeterEngine.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/engine/ClientJMeterEngine.java?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/engine/ClientJMeterEngine.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/engine/ClientJMeterEngine.java Tue Jan 23 20:50:52 2018
@@ -19,14 +19,15 @@
package org.apache.jmeter.engine;
import java.io.File;
-import java.net.MalformedURLException;
-import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
import java.rmi.server.RemoteObject;
import java.util.Properties;
+import org.apache.jmeter.rmi.RmiUtils;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.util.JMeterUtils;
@@ -46,26 +47,41 @@ public class ClientJMeterEngine implemen
private HashTree test;
- private final String host;
+ /**
+ * Maybe only host or host:port
+ */
+ private final String hostAndPort;
- private static RemoteJMeterEngine getEngine(String h) throws MalformedURLException, RemoteException,
- NotBoundException {
- final String name = "//" + h + "/" + RemoteJMeterEngineImpl.JMETER_ENGINE_RMI_NAME; // $NON-NLS-1$ $NON-NLS-2$
- Remote remobj = Naming.lookup(name);
- if (remobj instanceof RemoteJMeterEngine){
- final RemoteJMeterEngine rje = (RemoteJMeterEngine) remobj;
- if (remobj instanceof RemoteObject){
- RemoteObject robj = (RemoteObject) remobj;
- System.out.println("Using remote object: "+robj.getRef().remoteToString());
- }
- return rje;
- }
- throw new RemoteException("Could not find "+name);
- }
-
- public ClientJMeterEngine(String host) throws MalformedURLException, NotBoundException, RemoteException {
- this.remote = getEngine(host);
- this.host = host;
+ private static RemoteJMeterEngine getEngine(String hostAndPort)
+ throws RemoteException, NotBoundException {
+ final String name = RemoteJMeterEngineImpl.JMETER_ENGINE_RMI_NAME; // $NON-NLS-1$ $NON-NLS-2$
+ String host = hostAndPort;
+ int port = RmiUtils.DEFAULT_RMI_PORT;
+ int indexOfSeparator = hostAndPort.indexOf(':');
+ if (indexOfSeparator >= 0) {
+ host = hostAndPort.substring(0, indexOfSeparator);
+ String portAsString = hostAndPort.substring(indexOfSeparator+1);
+ port = Integer.parseInt(portAsString);
+ }
+ Registry registry = LocateRegistry.getRegistry(
+ host,
+ port,
+ RmiUtils.createClientSocketFactory());
+ Remote remobj = registry.lookup(name);
+ if (remobj instanceof RemoteJMeterEngine){
+ final RemoteJMeterEngine rje = (RemoteJMeterEngine) remobj;
+ if (remobj instanceof RemoteObject){
+ RemoteObject robj = (RemoteObject) remobj;
+ System.out.println("Using remote object: "+robj.getRef().remoteToString()); // NOSONAR
+ }
+ return rje;
+ }
+ throw new RemoteException("Could not find "+name);
+ }
+
+ public ClientJMeterEngine(String hostAndPort) throws NotBoundException, RemoteException {
+ this.remote = getEngine(hostAndPort);
+ this.hostAndPort = hostAndPort;
}
/** {@inheritDoc} */
@@ -79,7 +95,7 @@ public class ClientJMeterEngine implemen
/** {@inheritDoc} */
@Override
public void stopTest(boolean now) {
- log.info("about to "+(now ? "stop" : "shutdown")+" remote test on "+host);
+ log.info("about to {} remote test on {}", (now ? "stop" : "shutdown"), hostAndPort);
try {
remote.rstopTest(now);
} catch (Exception ex) {
@@ -95,7 +111,7 @@ public class ClientJMeterEngine implemen
remote.rreset();
} catch (java.rmi.ConnectException e) {
log.info("Retry reset after: "+e);
- remote = getEngine(host);
+ remote = getEngine(hostAndPort);
remote.rreset();
}
} catch (Exception ex) {
@@ -132,13 +148,13 @@ public class ClientJMeterEngine implemen
synchronized(LOCK)
{
methodName="rconfigure()"; // NOSONAR Used for tracing
- remote.rconfigure(testTree, host, baseDirRelative, scriptName);
+ remote.rconfigure(testTree, hostAndPort, baseDirRelative, scriptName);
}
- log.info("sent test to " + host + " basedir='"+baseDirRelative+"'"); // $NON-NLS-1$
+ log.info("sent test to {} basedir='{}'", hostAndPort, baseDirRelative); // $NON-NLS-1$
if(savep == null) {
savep = new Properties();
}
- log.info("Sending properties "+savep);
+ log.info("Sending properties {}", savep);
try {
methodName="rsetProperties()";// NOSONAR Used for tracing
remote.rsetProperties(savep);
@@ -147,9 +163,9 @@ public class ClientJMeterEngine implemen
}
methodName="rrunTest()";
remote.rrunTest();
- log.info("sent run command to "+ host);
+ log.info("sent run command to {}", hostAndPort);
} catch (IllegalStateException ex) {
- log.error("Error in "+methodName+" method "+ex); // $NON-NLS-1$ $NON-NLS-2$
+ log.error("Error in {} method ", methodName, ex); // $NON-NLS-1$ $NON-NLS-2$
tidyRMI(log);
throw ex; // Don't wrap this error - display it as is
} catch (Exception ex) {
@@ -179,7 +195,7 @@ public class ClientJMeterEngine implemen
// Called by JMeter ListenToTest if remoteStop is true
@Override
public void exit() {
- log.info("about to exit remote server on {}", host);
+ log.info("about to exit remote server on {}", hostAndPort);
try {
remote.rexit();
} catch (RemoteException e) {
@@ -201,7 +217,10 @@ public class ClientJMeterEngine implemen
return true;
}
+ /**
+ * @return host or host:port
+ */
public String getHost() {
- return host;
+ return hostAndPort;
}
}
Modified: jmeter/trunk/src/core/org/apache/jmeter/engine/RemoteJMeterEngineImpl.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/engine/RemoteJMeterEngineImpl.java?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/engine/RemoteJMeterEngineImpl.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/engine/RemoteJMeterEngineImpl.java Tue Jan 23 20:50:52 2018
@@ -20,7 +20,6 @@ package org.apache.jmeter.engine;
import java.io.File;
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
@@ -28,6 +27,7 @@ import java.rmi.registry.Registry;
import java.rmi.server.ServerNotActiveException;
import java.util.Properties;
+import org.apache.jmeter.rmi.RmiUtils;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
@@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
* This is the JMeter server main code.
*/
public final class RemoteJMeterEngineImpl extends java.rmi.server.UnicastRemoteObject implements RemoteJMeterEngine {
- private static final long serialVersionUID = 241L;
+ private static final long serialVersionUID = 242L;
private static final Logger log = LoggerFactory.getLogger(RemoteJMeterEngineImpl.class);
@@ -48,63 +48,36 @@ public final class RemoteJMeterEngineImp
private transient Thread ownerThread;
- private static final int DEFAULT_RMI_PORT =
- JMeterUtils.getPropDefault("server.rmi.port", 1099); // $NON-NLS-1$
-
- private static final int DEFAULT_LOCAL_PORT =
- JMeterUtils.getPropDefault("server.rmi.localport", 0); // $NON-NLS-1$
-
- static{
- if (DEFAULT_LOCAL_PORT != 0){
- System.out.println("Using local port: "+DEFAULT_LOCAL_PORT);
- }
- }
-
// Should we create our own copy of the RMI registry?
private static final boolean CREATE_SERVER =
JMeterUtils.getPropDefault("server.rmi.create", true); // $NON-NLS-1$
private final Object LOCK = new Object();
- private final int rmiPort;
+ /**
+ * RMI Registry port
+ */
+ private final int rmiRegistryPort;
private Properties remotelySetProperties;
- private RemoteJMeterEngineImpl(int localPort, int rmiPort) throws RemoteException {
- super(localPort); // Create this object using the specified port (0 means anonymous)
- this.rmiPort = rmiPort;
+ private RemoteJMeterEngineImpl(int localPort, int rmiRegistryPort) throws RemoteException {
+ super(localPort, RmiUtils.createClientSocketFactory(), RmiUtils.createServerSocketFactory()); // Create this object using the specified port (0 means anonymous)
+ this.rmiRegistryPort = rmiRegistryPort;
System.out.println("Created remote object: "+this.getRef().remoteToString());
}
- public static void startServer(int rmiPort) throws RemoteException {
- RemoteJMeterEngineImpl engine = new RemoteJMeterEngineImpl(DEFAULT_LOCAL_PORT, rmiPort == 0 ? DEFAULT_RMI_PORT : rmiPort);
+ public static void startServer(int rmiRegistryPort) throws RemoteException {
+ RemoteJMeterEngineImpl engine =
+ new RemoteJMeterEngineImpl(
+ RmiUtils.DEFAULT_LOCAL_PORT,
+ rmiRegistryPort);
engine.init();
}
private void init() throws RemoteException {
- log.info("Starting backing engine on {}", this.rmiPort);
- InetAddress localHost=null;
- // Bug 47980 - allow override of local hostname
- String host = System.getProperties().getProperty("java.rmi.server.hostname"); // $NON-NLS-1$
- try {
- if( host==null ) {
- log.info("System property 'java.rmi.server.hostname' is not defined, using localHost address");
- localHost = InetAddress.getLocalHost();
- } else {
- log.info("Resolving by name the value of System property 'java.rmi.server.hostname': {}", host);
- localHost = InetAddress.getByName(host);
- }
- } catch (UnknownHostException e1) {
- throw new RemoteException("Cannot start. Unable to get local host IP address.", e1);
- }
- if (log.isInfoEnabled()) {
- log.info("Local IP address={}", localHost.getHostAddress());
- }
- // BUG 52469 : Allow loopback address for SSH Tunneling of RMI traffic
- if (host == null && localHost.isLoopbackAddress()){
- String hostName = localHost.getHostName();
- throw new RemoteException("Cannot start. "+hostName+" is a loopback address.");
- }
+ log.info("Starting backing engine on {}", this.rmiRegistryPort);
+ InetAddress localHost = RmiUtils.getRmiHost();
if (localHost.isSiteLocalAddress()){
// should perhaps be log.warn, but this causes the client-server test to fail
log.info("IP address is a site-local address; this may cause problems with remote access.\n"
@@ -115,7 +88,9 @@ public final class RemoteJMeterEngineImp
if (CREATE_SERVER){
log.info("Creating RMI registry (server.rmi.create=true)");
try {
- reg = LocateRegistry.createRegistry(this.rmiPort);
+ reg = LocateRegistry.createRegistry(this.rmiRegistryPort,
+ RmiUtils.createClientSocketFactory(),
+ RmiUtils.createServerSocketFactory());
log.debug("Created registry: {}", reg);
} catch (RemoteException e){
String msg="Problem creating registry: "+e;
@@ -127,11 +102,14 @@ public final class RemoteJMeterEngineImp
try {
if (reg == null) {
log.debug("Locating registry");
- reg = LocateRegistry.getRegistry(this.rmiPort);
+ reg = LocateRegistry.getRegistry(
+ RmiUtils.getRmiHost().getHostName(),
+ this.rmiRegistryPort,
+ RmiUtils.createClientSocketFactory());
}
log.debug("About to rebind registry: {}", reg);
reg.rebind(JMETER_ENGINE_RMI_NAME, this);
- log.info("Bound to registry on port {}", this.rmiPort);
+ log.info("Bound to RMI registry on port {}", this.rmiRegistryPort);
} catch (Exception ex) {
log.error("rmiregistry needs to be running to start JMeter in server mode. {}", ex.toString());
// Throw an Exception to ensure caller knows ...
@@ -170,14 +148,14 @@ public final class RemoteJMeterEngineImp
}
@Override
- public void rrunTest() throws RemoteException, JMeterEngineException, IllegalStateException {
+ public void rrunTest() throws RemoteException, JMeterEngineException {
log.info("Running test");
checkOwner("runTest");
backingEngine.runTest();
}
@Override
- public void rreset() throws RemoteException, IllegalStateException {
+ public void rreset() throws RemoteException {
// Mail on userlist reported NPE here - looks like only happens if there are network errors, but check anyway
if (backingEngine != null) {
log.info("Reset");
@@ -216,7 +194,10 @@ public final class RemoteJMeterEngineImp
};
et.setDaemon(false);
// Tidy up any objects we created
- Registry reg = LocateRegistry.getRegistry(this.rmiPort);
+ Registry reg = LocateRegistry.getRegistry(
+ RmiUtils.getRmiHost().getHostName(),
+ this.rmiRegistryPort,
+ RmiUtils.createClientSocketFactory());
try {
reg.unbind(JMETER_ENGINE_RMI_NAME);
} catch (NotBoundException e) {
@@ -229,7 +210,7 @@ public final class RemoteJMeterEngineImp
}
@Override
- public void rsetProperties(Properties p) throws RemoteException, IllegalStateException {
+ public void rsetProperties(Properties p) throws RemoteException {
checkOwner("setProperties");
if(remotelySetProperties != null) {
Properties jmeterProperties = JMeterUtils.getJMeterProperties();
@@ -247,11 +228,11 @@ public final class RemoteJMeterEngineImp
* @param methodName the name of the method for the log message
* @throws IllegalStateException if the caller is not the owner.
*/
- private void checkOwner(String methodName) throws IllegalStateException {
+ private void checkOwner(String methodName) {
if (ownerThread != null && ownerThread != Thread.currentThread()){
String msg = "The engine is not owned by this thread - cannot call "+methodName;
log.warn(msg);
- throw new IllegalStateException(msg);
+ throw new IllegalStateException(msg);
}
}
}
Added: jmeter/trunk/src/core/org/apache/jmeter/rmi/AliasKeyManager.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/rmi/AliasKeyManager.java?rev=1822052&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/rmi/AliasKeyManager.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/rmi/AliasKeyManager.java Tue Jan 23 20:50:52 2018
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.jmeter.rmi;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.X509KeyManager;
+
+/**
+ * Implementation of {@link KeyManager} that allows using an alias
+ * @since 4.0
+ *
+ */
+public class AliasKeyManager implements X509KeyManager {
+
+ private final String alias;
+ private final X509KeyManager km;
+
+ public AliasKeyManager(X509KeyManager km, String alias) {
+ this.km = km;
+ this.alias = alias;
+ }
+
+ /**
+ * Wraps the first found {@link X509KeyManager} that has a private key for
+ * the given {@code alias} as an {@link AliasKeyManager} and returns it as
+ * the only element in a newly created array.
+ *
+ * @param kms
+ * the KeyManagers to be searched for the {@code alias}
+ * @param alias
+ * the name to be searched for
+ * @return an array with one {@link AliasKeyManager} that has a private key
+ * named {@code alias}
+ * @throws IllegalArgumentException
+ * if no valid KeyManager is found
+ */
+ public static AliasKeyManager[] wrap(KeyManager[] kms, String alias) {
+ AliasKeyManager validManager = Arrays.asList(kms).stream()
+ .filter(m -> m instanceof X509KeyManager)
+ .map(m -> (X509KeyManager) m)
+ .filter(m -> m.getPrivateKey(alias) != null)
+ .map(m -> new AliasKeyManager(m, alias)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(
+ "No key found for alias '" + alias + "'"));
+ return new AliasKeyManager[] { validManager };
+ }
+
+ @Override
+ public String chooseClientAlias(String[] keyType, Principal[] issuers,
+ Socket socket) {
+ return alias;
+ }
+
+ @Override
+ public String chooseServerAlias(String keyType, Principal[] issuers,
+ Socket socket) {
+ return alias;
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ return this.km.getCertificateChain(alias);
+ }
+
+ @Override
+ public String[] getClientAliases(String keyType, Principal[] issuers) {
+ return this.km.getClientAliases(keyType, issuers);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ return this.km.getPrivateKey(alias);
+ }
+
+ @Override
+ public String[] getServerAliases(String keyType, Principal[] issuers) {
+ return this.km.getServerAliases(keyType, issuers);
+ }
+
+}
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/AliasKeyManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/AliasKeyManager.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/core/org/apache/jmeter/rmi/RMIServerSocketFactoryImpl.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/rmi/RMIServerSocketFactoryImpl.java?rev=1822052&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/rmi/RMIServerSocketFactoryImpl.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/rmi/RMIServerSocketFactoryImpl.java Tue Jan 23 20:50:52 2018
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.rmi;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.rmi.server.RMIServerSocketFactory;
+
+import javax.net.ServerSocketFactory;
+
+/**
+ * Custom {@link RMIServerSocketFactory} that binds RMI to particular host/ip
+ * @since 4.0
+ */
+public class RMIServerSocketFactoryImpl implements RMIServerSocketFactory, Serializable {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 6106381149147208254L;
+ private final InetAddress localAddress;
+
+ /**
+ *
+ */
+ public RMIServerSocketFactoryImpl(final InetAddress pAddress) {
+ this.localAddress = pAddress;
+ }
+
+ /**
+ * Creates a server socket that listens on localAddress:port
+ * @param port
+ * @see java.rmi.server.RMIServerSocketFactory#createServerSocket(int)
+ */
+ @Override
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return ServerSocketFactory.getDefault().createServerSocket(port, 0, localAddress);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RMIServerSocketFactoryImpl other = (RMIServerSocketFactoryImpl) obj;
+ if (localAddress == null) {
+ if (other.localAddress != null)
+ return false;
+ } else if (!localAddress.equals(other.localAddress))
+ return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return (localAddress == null) ? 0 : localAddress.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "RMIServerSocketFactoryImpl(host=" + localAddress + ")";
+ }
+}
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/RMIServerSocketFactoryImpl.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/RMIServerSocketFactoryImpl.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/core/org/apache/jmeter/rmi/RmiUtils.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/rmi/RmiUtils.java?rev=1822052&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/rmi/RmiUtils.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/rmi/RmiUtils.java Tue Jan 23 20:50:52 2018
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.rmi;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.rmi.RemoteException;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RMIServerSocketFactory;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.apache.jmeter.util.JMeterUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * RMI Helper class
+ * @since 4.0
+ */
+public final class RmiUtils {
+ private static final Logger log = LoggerFactory.getLogger(RmiUtils.class);
+
+ public static final int DEFAULT_RMI_REGISTRY_PORT = JMeterUtils
+ .getPropDefault("server_port", 0); // $NON-NLS-1$
+
+ public static final int DEFAULT_RMI_PORT = JMeterUtils
+ .getPropDefault("server.rmi.port", 1099); // $NON-NLS-1$
+
+ public static final int DEFAULT_LOCAL_PORT = JMeterUtils
+ .getPropDefault("server.rmi.localport", 0); // $NON-NLS-1$
+
+ private static final String KEYSTORE_TYPE = JMeterUtils
+ .getPropDefault("server.rmi.ssl.keystore.type", "JKS");
+
+ private static final String KEYSTORE_FILE = JMeterUtils
+ .getPropDefault("server.rmi.ssl.keystore.file", "rmi_keystore.jks");
+
+ private static final String KEYSTORE_PASSWORD = JMeterUtils
+ .getPropDefault("server.rmi.ssl.keystore.password", "changeit");
+
+ private static final String KEYSTORE_ALIAS = JMeterUtils
+ .getPropDefault("server.rmi.ssl.keystore.alias", "rmi");
+
+ private static final String TRUSTSTORE_TYPE = JMeterUtils
+ .getPropDefault("server.rmi.ssl.truststore.type", KEYSTORE_TYPE);
+
+ private static final String TRUSTSTORE_FILE = JMeterUtils
+ .getPropDefault("server.rmi.ssl.truststore.file", KEYSTORE_FILE);
+
+ private static final String TRUSTSTORE_PASSWORD = JMeterUtils
+ .getPropDefault("server.rmi.ssl.truststore.password",
+ KEYSTORE_PASSWORD);
+
+ private static final boolean SSL_DISABLED = JMeterUtils
+ .getPropDefault("server.rmi.ssl.disable", false);
+
+ static{
+ if (DEFAULT_LOCAL_PORT != 0){
+ System.out.println("Using local port: " + DEFAULT_LOCAL_PORT);
+ }
+ }
+
+ private RmiUtils() {
+ super();
+ }
+
+ public static RMIClientSocketFactory createClientSocketFactory() {
+ if (StringUtils.isBlank(KEYSTORE_FILE)) {
+ Validate.validState(SSL_DISABLED,
+ "No keystore for RMI over SSL specified. Set 'server.rmi.ssl.disable' to true, if this is intentional,"
+ + "if not run create-rmi-keystore.bat/create-rmi-keystore.sh to create a keystore and distribute it on client and servers"
+ + "used for distributed testing.");
+ return null;
+ }
+ final SSLRMIClientSocketFactory factory = new SSLRMIClientSocketFactory();
+ factory.setAlias(KEYSTORE_ALIAS);
+ factory.setKeystore(KEYSTORE_FILE, KEYSTORE_TYPE, KEYSTORE_PASSWORD);
+ factory.setTruststore(TRUSTSTORE_FILE, TRUSTSTORE_TYPE, TRUSTSTORE_PASSWORD);
+ return factory;
+ }
+
+ public static RMIServerSocketFactory createServerSocketFactory() throws RemoteException {
+ if (StringUtils.isBlank(KEYSTORE_FILE)) {
+ Validate.validState(SSL_DISABLED,
+ "No keystore for RMI over SSL specified. Set 'server.rmi.ssl.disable' to true, if this is intentional.");
+ return new RMIServerSocketFactoryImpl(getRmiHost());
+ }
+ SSLRMIServerSocketFactory factory = new SSLRMIServerSocketFactory(getRmiHost());
+ factory.setAlias(KEYSTORE_ALIAS);
+ factory.setKeystore(KEYSTORE_FILE, KEYSTORE_TYPE, KEYSTORE_PASSWORD);
+ factory.setTruststore(TRUSTSTORE_FILE, TRUSTSTORE_TYPE, TRUSTSTORE_PASSWORD);
+ return factory;
+ }
+
+ /**
+ * @return
+ * @throws RemoteException
+ */
+ public static InetAddress getRmiHost() throws RemoteException {
+ InetAddress localHost=null;
+ // Bug 47980 - allow override of local hostname
+ String host = System.getProperties().getProperty("java.rmi.server.hostname"); // $NON-NLS-1$
+ try {
+ if( host==null ) {
+ log.info("System property 'java.rmi.server.hostname' is not defined, using localHost address");
+ localHost = InetAddress.getLocalHost();
+ } else {
+ log.info("Resolving by name the value of System property 'java.rmi.server.hostname': {}", host);
+ localHost = InetAddress.getByName(host);
+ }
+ } catch (UnknownHostException e) {
+ throw new RemoteException("Cannot start. Unable to get local host IP address.", e);
+ }
+ if (log.isInfoEnabled()) {
+ log.info("Local IP address={}", localHost.getHostAddress());
+ }
+ // BUG 52469 : Allow loopback address for SSH Tunneling of RMI traffic
+ if (host == null && localHost.isLoopbackAddress()){
+ String hostName = localHost.getHostName();
+ throw new RemoteException("Cannot start. " + hostName + " is a loopback address.");
+ }
+ return localHost;
+ }
+
+ /**
+ *
+ * @return port of RMI Registry
+ */
+ public static int getRmiRegistryPort() {
+ return DEFAULT_RMI_REGISTRY_PORT == 0 ?
+ RmiUtils.DEFAULT_RMI_PORT : DEFAULT_RMI_REGISTRY_PORT;
+ }
+}
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/RmiUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/RmiUtils.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIClientSocketFactory.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIClientSocketFactory.java?rev=1822052&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIClientSocketFactory.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIClientSocketFactory.java Tue Jan 23 20:50:52 2018
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.rmi;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.Socket;
+import java.rmi.server.RMIClientSocketFactory;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+/**
+ * Implementation of {@link RMIClientSocketFactory} that:
+ * <ul>
+ * <li>Establishes SSL connection</li>
+ * </ul>
+ * @since 4.0
+ */
+public class SSLRMIClientSocketFactory
+ implements RMIClientSocketFactory, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String alias;
+ private String keyStoreLocation;
+ private String keyStorePassword;
+ private String keyStoreType;
+ private String trustStoreLocation;
+ private String trustStorePassword;
+ private String trustStoreType;
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public void setKeystore(String location, String type, String password) {
+ this.keyStoreLocation = location;
+ this.keyStoreType = type;
+ this.keyStorePassword = password;
+ }
+
+ public void setTruststore(String location, String type, String password) {
+ this.trustStoreLocation = location;
+ this.trustStoreType = type;
+ this.trustStorePassword = password;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException {
+
+ char[] passphrase = null;
+ if (keyStorePassword != null) {
+ passphrase = keyStorePassword.toCharArray();
+ }
+
+ KeyStore keyStore = null;
+ if (keyStoreLocation != null) {
+ keyStore = loadStore(keyStoreLocation, passphrase, keyStoreType);
+ }
+
+ KeyStore trustStore;
+ if (trustStoreLocation != null) {
+ trustStore = loadStore(trustStoreLocation, trustStorePassword.toCharArray(), trustStoreType);
+ } else {
+ trustStore = keyStore;
+ }
+
+ if (alias == null) {
+ throw new IOException(
+ "SSL certificate alias cannot be null; MUST be set for SSLServerSocketFactory!");
+ }
+
+ KeyManagerFactory kmf;
+ SSLContext ctx;
+ try {
+ kmf = KeyManagerFactory
+ .getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, passphrase);
+ ctx = SSLContext.getInstance("TLS");
+ TrustManagerFactory tmf = TrustManagerFactory
+ .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(trustStore);
+ ctx.init(AliasKeyManager.wrap(kmf.getKeyManagers(), alias), tmf.getTrustManagers(), null);
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e);
+ }
+ SSLSocketFactory factory = ctx.getSocketFactory();
+ if (factory == null) {
+ throw new IOException(
+ "Unable to obtain SSLSocketFactory for provided KeyStore");
+ }
+
+ return factory.createSocket(host, port);
+ }
+
+ private KeyStore loadStore(String location, char[] passphrase, String type)
+ throws IOException {
+ try {
+ KeyStore store = KeyStore.getInstance(type);
+ store.load(new FileInputStream(location), passphrase);
+ return store;
+ } catch (NoSuchAlgorithmException | CertificateException
+ | KeyStoreException e) {
+ throw new IOException("Can't load " + location + " as type " + type, e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SSLRMIClientSocketFactory(keyStoreLocation=" + this.keyStoreLocation + ", type="
+ + this.keyStoreType +", trustStoreLocation=" + this.trustStoreLocation + ", type="
+ + this.trustStoreType + ", alias=" + this.alias + ')';
+ }
+}
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIClientSocketFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIClientSocketFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIServerSocketFactory.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIServerSocketFactory.java?rev=1822052&view=auto
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIServerSocketFactory.java (added)
+++ jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIServerSocketFactory.java Tue Jan 23 20:50:52 2018
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.jmeter.rmi;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.rmi.server.RMIServerSocketFactory;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link RMIServerSocketFactoryImpl} that:
+ * <ul>
+ * <li>Binds socket to an address</li>
+ * <li>Establishes SSL connection</li>
+ * </ul>
+ * @since 4.0
+ */
+public class SSLRMIServerSocketFactory implements RMIServerSocketFactory, Serializable {
+
+ private static final long serialVersionUID = 258730225720182190L;
+ private static final Logger LOGGER = LoggerFactory.getLogger(SSLRMIServerSocketFactory.class);
+ private final InetAddress localAddress;
+
+ private String alias;
+ private String keyStoreLocation;
+ private String keyStorePassword;
+ private String keyStoreType;
+ private String trustStoreLocation;
+ private String trustStorePassword;
+ private String trustStoreType;
+
+ private boolean clientAuth;
+
+ public SSLRMIServerSocketFactory(InetAddress pAddress) {
+ this.localAddress = pAddress;
+ }
+
+ public void setNeedClientAuth(boolean clientAuth) {
+ this.clientAuth = clientAuth;
+ }
+
+ public void setKeystore(String location, String type, String password) {
+ this.keyStoreLocation = location;
+ this.keyStoreType = type;
+ this.keyStorePassword = password;
+ }
+
+ public void setTruststore(String location, String type, String password) {
+ this.trustStoreLocation = location;
+ this.trustStoreType = type;
+ this.trustStorePassword = password;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ @Override
+ public ServerSocket createServerSocket(int port) throws IOException {
+ char[] passphrase = null;
+ if (keyStorePassword != null) {
+ passphrase = keyStorePassword.toCharArray();
+ }
+
+ KeyStore keyStore = null;
+ if (keyStoreLocation != null) {
+ keyStore = loadStore(keyStoreLocation, passphrase, keyStoreType);
+ }
+
+ KeyStore trustStore;
+ if (trustStoreLocation != null) {
+ trustStore = loadStore(trustStoreLocation, trustStorePassword.toCharArray(), trustStoreType);
+ } else {
+ trustStore = keyStore;
+ }
+
+ if (alias == null) {
+ throw new IOException(
+ "SSL certificate alias cannot be null; MUST be set for SSLServerSocketFactory!");
+ }
+
+ SSLContext ctx;
+ try {
+ KeyManagerFactory kmf = KeyManagerFactory
+ .getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ kmf.init(keyStore, passphrase);
+ TrustManagerFactory tmf = TrustManagerFactory
+ .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(trustStore);
+ ctx = SSLContext.getInstance("TLS");
+ ctx.init(AliasKeyManager.wrap(kmf.getKeyManagers(), alias), tmf.getTrustManagers(), null);
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e);
+ }
+ SSLServerSocketFactory factory = ctx.getServerSocketFactory();
+ if (factory == null) {
+ throw new IOException(
+ "Unable to obtain SSLServerSocketFactory for provided KeyStore");
+ }
+
+ SSLServerSocket socket = (SSLServerSocket) factory
+ .createServerSocket(port, 0, localAddress);
+ socket.setNeedClientAuth(clientAuth);
+ LOGGER.info("Created SSLSocket: {}", socket);
+ return socket;
+ }
+
+ private KeyStore loadStore(String location, char[] passphrase, String type)
+ throws IOException {
+ try (FileInputStream fileInputStream = new FileInputStream(location)){
+ KeyStore store = KeyStore.getInstance(type);
+ store.load(fileInputStream, passphrase);
+ return store;
+ } catch (NoSuchAlgorithmException | CertificateException
+ | KeyStoreException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SSLRMIServerSocketFactory(host=" + localAddress
+ + ", keyStoreLocation=" + this.keyStoreLocation + ", type="
+ + this.keyStoreType +", trustStoreLocation=" + this.trustStoreLocation + ", type="
+ + this.trustStoreType + ", alias=" + this.alias + ')';
+ }
+
+}
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIServerSocketFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jmeter/trunk/src/core/org/apache/jmeter/rmi/SSLRMIServerSocketFactory.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: jmeter/trunk/src/core/org/apache/jmeter/samplers/RemoteSampleListenerImpl.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/samplers/RemoteSampleListenerImpl.java?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/samplers/RemoteSampleListenerImpl.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/samplers/RemoteSampleListenerImpl.java Tue Jan 23 20:50:52 2018
@@ -21,6 +21,7 @@ package org.apache.jmeter.samplers;
import java.rmi.RemoteException;
import java.util.List;
+import org.apache.jmeter.rmi.RmiUtils;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.util.JMeterUtils;
@@ -40,7 +41,7 @@ public class RemoteSampleListenerImpl ex
JMeterUtils.getPropDefault("client.rmi.localport", 0); // $NON-NLS-1$
public RemoteSampleListenerImpl(Object listener) throws RemoteException {
- super(DEFAULT_LOCAL_PORT);
+ super(DEFAULT_LOCAL_PORT, RmiUtils.createClientSocketFactory(), RmiUtils.createServerSocketFactory());
if (listener instanceof TestStateListener) {
testListener = (TestStateListener) listener;
} else {
Modified: jmeter/trunk/src/core/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java
URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/src/core/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java (original)
+++ jmeter/trunk/src/core/org/apache/jmeter/threads/RemoteThreadsListenerImpl.java Tue Jan 23 20:50:52 2018
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
import org.apache.jmeter.gui.GuiPackage;
+import org.apache.jmeter.rmi.RmiUtils;
import org.apache.jmeter.testelement.ThreadListener;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.reflect.ClassFinder;
@@ -55,7 +56,7 @@ public class RemoteThreadsListenerImpl e
* @throws RemoteException if failed to export object
*/
public RemoteThreadsListenerImpl() throws RemoteException {
- super(DEFAULT_LOCAL_PORT);
+ super(DEFAULT_LOCAL_PORT, RmiUtils.createClientSocketFactory(), RmiUtils.createServerSocketFactory());
try {
List<String> listClasses = ClassFinder.findClassesThatExtend(
JMeterUtils.getSearchPaths(),
Modified: jmeter/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1822052&r1=1822051&r2=1822052&view=diff
==============================================================================
--- jmeter/trunk/xdocs/changes.xml [utf-8] (original)
+++ jmeter/trunk/xdocs/changes.xml [utf-8] Tue Jan 23 20:50:52 2018
@@ -275,7 +275,7 @@ itself does not have to be edited anymor
<li><bug>56368</bug>Create and Deploy source artifacts to Maven central</li>
<li><bug>61973</bug>Create and Deploy javadoc artifacts to Maven central</li>
<li><pr>371</pr>Fix example in documentation for <complink name="XPath Assertion"/>. Contributed by Konstantin Kalinin (kkalinin at hotmail.com)</li>
- <li><bug>62026</bug>Use a <code>setenv.sh</code> script if available to preset variables when starting JMeter with <code>bin/jmeter</code>.</li>
+ <li><bug>62039</bug>Distributed testing : Provide ability to use SSL</li>
</ul>
<ch_section>Non-functional changes</ch_section>