In this section:
Requirements
Java 5.0 - 8.0
Benefits
Not all components of a system under test communicate over a defined wire protocol or an API that can be virtualized by easily reconfiguring the application under test (AUT) to direct traffic to a different host or a different endpoint. In some cases, integration takes place via APIs that cannot be controlled without modifying the application code directly.
For teams working with Java AUTs, Parasoft's Java Virtualization agent can be used to record Java method calls directly within the application JVM and virtualize the behavior of these calls. The Parasoft Java Virtualization agent is particularly useful for virtualizing RMI, remote EJB calls, or any other internally-developed code that provides functionality and/or connectivity to a dependent system.
Methodology
The AUT is launched with the Parasoft agent configured as a JVM startup parameter. The configuration for that agent indicates whether it is in inactive, record, or virtualize mode for each of the specified target classes and methods.
Once the agent is in record mode, you can exercise the AUT with the behavior you want to record. The agent will save method invocation data into a local file. To create a virtual asset that represents the recorded behavior, you can provide that file to Virtualize's virtual asset creation wizard.
After the desired behavior is recorded, you can switch the agent to virtualize mode and configure it with the Virtualize virtual asset's HTTP endpoint. Now, the same method calls will be redirected to the Virtualize PVA instead of the actual method; they will return the object instances that were previously recorded.
Here is a sample PVA that was recorded on the Oracle WebLogic medrec sample application:
Java Virtualization Patterns
There are two distinct virtualization patterns that can be used to record Java method invocations and virtualize their return values:
- Class Method Virtualization: Virtualizes return values for callers of a class.
- Call Virtualization: Virtualizes how certain methods get invoked (or called) from specific places.
The best pattern to apply depends on the application needs. You can mix the two virtualization patterns, as well as mix various modes for each of the specified classes/methods.
Class Method Virtualization
In this pattern, call callers of class V receive virtualized return values. Essentially, this globally alters the behavior of class V regardless of how (and from where) its virtualized methods get invoked.
To configure this type of virtualization, you provide the fully-qualified class name as well as the name of the methods that you want to virtualize (or record, when in record mode). The return values of these specified methods will be altered when in virtualize mode.
Call Virtualization
In this pattern, you virtualize how certain methods get invoked (or called) from specific places. Here, you provide:
- The fully qualified name of the calling class. This is the class where virtualization on method calls into some other classes and methods will be applied.
- The list of methods within the calling class from which virtualization is going to be applied. When these methods call specific other objects and methods (the items below), they will be get virtualized.
- The fully qualified name of the class type to be virtualized from within the calling class. This is the declaring interface or object type on which to apply the virtualization.
- The list of methods on which to apply the virtualization.
This pattern is particularly useful for remote EJB method invocation virtualization. The calling class would typically be a controller object. Within that object, remote EJB invocations are made using some declared remote interface type. Here is an illustration of how remote EJB calls can be virtualized using this pattern:
Agent Configuration
Configuring the Java Application to Use the Agent
- Copy
ParasoftJavaVirtAgent.jar
and the other accompanying files to a file path location that can be accessed by your application. Add the following JVM argument to your application’s “java” startup executable (provide as a single line with a space before each –D argument):
-javaagent:"C:/PathTo/ParasoftJavaVirtAgent.jar" -Dparasoft.virtualize.config="C:/Path/AgentConfig.xml" -Dparasoft.virtualize.log.config="C:/Path/logging.properties"
If your application logs ClassNotFoundExceptions, then try adding the following additional flags:
-Xbootclasspath/a:"C:/Path/ParasoftJavaVirtAgent.jar;C:/Path/xstream-1.4.2.jar"
Oracle WebLogic
Edit the setDomainEnv.sh server launch script in order to add the above arguments. Setting Xbootclasspath is not needed.
Apache Karaf/ServiceMix
Setting Xbootclasspath is necessary. You also need to adjust the Java packages that are delegated to the parent class loader. This ensures that your OSGi bundles have runtime visibility to Parasoft Agent runtime libraries. In Apache Service Mix, this can be done by appending the following content (all one line) to the end of the org.osgi.framework.bootdelegation property within /etc/custom.properties:
,com.parasoft.virtualize.java.agent.runtime,com.parasoft.virtualize.java.agent.runtime.*
,com.thoughtworks.xstream.*
IBM WebSphere Application Server
There is no need to edit server startup files. You may configure the agent using IBM Integrated Solutions Console for WAS. Navigate to Application servers > [server name] > Process definition >Java Virtual Machine. In the "Generic JVM Arguments" field, provide the agent configuration arguments. For example(all on one line, with a space separating each –D argument):
-javaagent:C:/Parasoft/ParasoftJavaVirtAgent.jar -Dparasoft.virtualize.config=C:/Parasoft/PlantsByWebSphere.xml -Dparasoft.virtualize.log.config=C:/Parasoft/logging.properties
If the -javaagent path is mistyped or the file is removed and the server fails to start, you can fix it by editing [was.install.root]/profiles/[profile name]/config/cells/[cellname]/nodes/[node name]/servers/server1/server.xml and removing the bad argument (search for occurrences of ParasoftJavaVirtAgent.jar or other argument parameters you have provided).
WebSphere Application Server will override the logging configuration of the agent and have it always redirect output to its own logging framework. You can configure the desired logging level using the IBM Console under Logging and Tracing > server1 > Change log detail levels. Look for the com.parasoft.virtualize.* component and adjust the logging level as desired. Parasoft Java Virtualization agent logs can be found under [was.install.root]/profiles/[profilename]/logs/[server name]/native_stdout.log.
IBM WebSphere Community Edition
Setting Xbootclasspath
is necessary.
Configuring the Agent to be Controlled by a Virtual Asset
Deploy the JavaVirtualization.pva
(included in the zip archive) to a Virtualize server and set the agent's Virtualize Server URLto point to that asset. This asset will then be able to:
- Receive messages from the Java agent.
- Respond to messages.
- Enable remote switching of the Java agent's mode at runtime.
- Receive logging messages from the agent.
- Receive the recorded traffic from the agent.
The following settings need to be configured on the agent:
-Dparasoft.virtualize.remote.mode=true
If true, enables switching modes at runtime. This switching is done by updating the environment being used by the JavaVirtualization.pva. Check the PVA to verify that you can control the mode by setting the appropriate environment to be active.
logging.properties
To enable sending java util logging to the server, set this to com.parasoft.virtualize.java.agent.log.ServerHandler
.
AgentConfig.xml
To have the agent send recorded traffic to the Virtualize server, set the "recorderType" in AgentConfig.xml to "HTTP_TRAFFIC_SERVER" (see the following section for more details on AgentConfig.xml).
Configuring Agent Logging
The agent includes a logging.properties
file, which can be edited in order to adjust the verbosity and location of the local log files that are created by the agent. Since the agent uses Java’s standard built-in logging framework, properties and settings that apply to that framework are applicable to the agent. (see http://docs.oracle.com/javase/7/docs/technotes/guides/logging/overview.html) In general, the following two properties should be considered:
com.parasoft.virtualize.java.agent.level=ALL|FINEST|FINER|FINE|INFO|WARNING|SEVERE
java.util.logging.FileHandler.pattern=C:\path/parasoft_java_agent_log.%g.txt
Note that some application server platforms will override the logging destination and level set by the agent's logging.properties file. In these cases, you can adjust the logging level using your application server's own framework. Refer to notes on specific server configurations in the "Configuring the Java application to use the agent" section (above).
By default, if the application JVM is successfully started with the agent, the following log statement will be shown:
[Parasoft Java Virtualization Agent: INFO {timestamp}] class file transformer initialized using configuration file {file path}
Once the application is loaded and instrumented, one or more of the following log statements should be present under the default (info) logging level:
[Parasoft Java Virtualization Agent: INFO {timestamp}] transforming {class name provided in the agent configuration XML}
Overview of AgentConfig.xml
AgentConfig.xml is an XML descriptor that is specific to the agent. It tells the agent which classes and methods to apply to—and in which mode (virtualize, record, etc.). Here is a sample configuration file:
<ParasoftJavaVirtAgent> <virtualizeServer> <url>http://localhost:9080/ParaBankJavaLoanRequest</url> </virtualizeServer> <recorderType>HTTP_TRAFFIC_FILE</recorderType> <localRecordingFilePath>/Users/jsmith/JavaVirt/JavaRecording.txt</localRecordingFilePath> <callVirtualizationClasses> <callVirtualizationClass> <callingMethods> <method> <className>com.parasoft.parabank.domain.logic.impl.BankManagerImpl</className> <methodName>requestLoan</methodName> <argumentTypes> <string></string> </argumentTypes> <mode>INACTIVE</mode> </method> </callingMethods> <virtualizedMethods> <method> <className>com.parasoft.parabank.domain.logic.LoanProvider</className> <methodName>requestLoan</methodName> <argumentTypes> <string></string> </argumentTypes> <mode>INACTIVE</mode> </method> </virtualizedMethods> </callVirtualizationClass> </callVirtualizationClasses> <methodVirtualizationClasses> <method> <className>com.parasoft.parabank.domain.logic.impl.ConfigurableLoanProvider</className> <methodName>requestLoan</methodName> <argumentTypes> <string></string> </argumentTypes> <mode>RECORD</mode> </method></methodVirtualizationClasses> </ParasoftJavaVirtAgent>
Agent Configuration Sections
<virtualizeServer> <url>http://virtServerHost:9080/ParaBankJavaLoanRequest</url> </virtualizeServer>
is applicable to VIRTUALIZE mode methods. It indicates the HTTP path to the Virtualize server and the PVA that has been previously generated from a recording.
<localRecordingFilePath>/Users/jsmith/JavaVirt/JavaRecording.txt</localRecordingFilePath>
is the absolute path to where the Java method invocation should be saved (when in RECORD mode). Note that the default is to append new traffic to existing traffic files. Thus, recording the same call multiple times can lead to duplicate data in the file.
<callVirtualizationClasses> </callVirtualizationClasses>
can include one or more <callVirtualizationClass
> elements. It specifies the classes and methods for the call virtualization pattern. Note that specifying the fully-qualified signatures for the methods using
<argumentTypes><string>argTypesNYI</string></argumentTypes>
is not supported at this point.
The <callingMethods>
elements specify from which methods the call virtualization should be applied. The methods specified by the <virtualizedMethods>
elements are the methods that are actually virtualized.
<mode> </mode>
can take one of three values—INACTIVE, RECORD or VIRTUALIZE—for any of the methods specified. INACTIVE tells the agent that it should let the original method get invoked without recording its invocation, virtualizing it, or otherwise altering its behavior.
<methodVirtualizationClasses>
specifies the list of classes and their methods where the Class Method Virtualization pattern should be applied.
As you can see from this example, it is possible to mix the two virtualization patterns and mix various modes for each of the specified classes/methods.
Generating the PVAs
Once the application is exercised with the desired scenarios and with some classes/methods configured with RECORD mode, the recorded file can be taken from the AUT and provided to Virtualize’s PVA creation wizard from traffic.
When creating PVAs from traffic, choose the HTTP option (the Java recording files produced by the agent are formatted as HTTP traffic files with the objects converted into an XML representation).
A responder will be created for each unique method because each recorded method will have a unique XML root element in the XML file. Each responder will be created in the "Multiple Responses" mode to reflect the various input arguments/return values that were recorded.
Using the Generated PVAs
Once the PVA is deployed, switch AgentConfig.xml to VIRTUALIZE mode and point the url element at the PVA deployment HTTP endpoint. The agent will cause the application’s JVM to redirect the specified method calls remotely to Virtualize (over HTTP) instead of calling the actual methods. The return objects that are configured in Virtualize will be used as the return values for these methods.
Assumptions and Limitations
The Java virtualization agent is particularly useful for recording and virtualizing stateless method calls that perform some function in another subcomponent within the application or call out remotely into some dependency.
Following are some of the constraints that should be kept in mind when using this release of the agent:
- Does not record object state.
- No per-instance controls –everything is at a class/object type level.
- Does not record/virtualize thrown exceptions.
- Calling method argument values do not impact virtualized returned values.
- Methods are identified by class name and method name; not yet with fully qualified signatures.
- Applications need to be restarted to apply mode changes.
Tutorial: ParaBank
Class Method Virtualization
First, let's try the Class Method Virtualization pattern by virtualizing the behavior of the ConfigurableLoanProvider class, which is invoked to request a loan regardless of what loan provider is configured in ParaBank.
- Create the ParaBank project within Virtualize.
- Use the AgentConfig.xml sample provided above (change localRecordingFilePath to the location where you wish to save the recording file in your environment).
Open the ParaBank Run Configuration and add the VM arguments (all on one line, with a space separating each –D argument):
-javaagent:"C:\PATH\ParasoftJavaVirtAgent.jar" -Dparasoft.virtualize.config="C:\PATH\AgentConfig.xml" -Dparasoft.virtualize.log.config="C:\PATH\logging.properties"
- Run the configuration, then exercise the application as follows:
- Log into ParaBank using john/demo.
- Click the Request Loan link.
- Request a loan with a 1000 principal and a 200 down payment.
- Request another 10000 loan with a 100 down payment.
- Verify that a file was created in the location you provided in local RecordingFilePath
.
Shutdown ParaBank.
- Create a new PVA from traffic as follows:
- Select Traffic> Generate Fixed Messages, specify the location of the traffic file, then click Next.
- Select Based on operation/type for message grouping, then click Next.
- During Message Grouping Review, disable the Autoconfig option to manually configure the request matching options in the following step, then click Next.
- During Request Matching, remove existing parameters, and add only the loanAmount and downPayment elements as the relevant parameters for the request body, then click Next.
Specify a deployment endpoint that will match what you have in the AgentConfig.xml. In this case, use:
/ParaBankJavaLoanRequest
<virtualizeServer> <url>http://localhost:9080/ParaBankJavaLoanRequest</url> </virtualizeServer>
- Click Finish.
The PVA will be created with a single responder in Multiple Responses mode. Only a single responder would be created because only one method was virtualized in this example.
- Edit the responses by adding the following:
<loanProviderName>Parasoft Virtualize</loanProviderName>
Switch the AgentConfig.xml mode to VIRTUALIZE by using:
<methodVirtualizationClasses> <method> <className>com.parasoft.parabank.domain.logic.impl.ConfigurableLoanProvider</className> <methodName>requestLoan</methodName> <argumentTypes> <string></string> </argumentTypes> <mode>VIRTUALIZE</mode> </method> </methodVirtualizationClasses>
- Run ParaBank again and request loans with the same amounts that were previously recorded. Notice how the Loan Provider label will display whatever you specified in the Virtualize responder. If you enable monitoring on the virtual asset within Virtualize, you will be able to see the requests and responses that are generated by ParaBank and received by Virtualize.
Call Virtualization
Next, let's try the Call Virtualization pattern. The goal of this exercise is to virtualize the following call line under BankManagerImpl.java, method requestLoan():
LoanResponse loanResponse = loanProvider.requestLoan(loanRequest);
To achieve this, we need to instruct the agent that:
- BankManagerImpl is the calling class.
- Its requestLoan() method is the calling method.
- Since the object type of loan Provider is
LoanProvider
then that is the virtualized class and the method call we want to virtualize here is requestLoan(). (The fact that the calling method name and the virtualized method name are the same in this example is just a coincidence.)
To try the Call Virtualization pattern:
Set the class method virtualization to INACTIVE.
<methodVirtualizationClasses> <method> <className>com.parasoft.parabank.domain.logic.impl.ConfigurableLoanProvider</className> <methodName>requestLoan</methodName> <argumentTypes> <string></string> </argumentTypes> <mode>INACTIVE</mode> </method> </methodVirtualizationClasses>
Set the call Virtualization class modes to RECORD.
<callVirtualizationClass> <callingMethods> <method> <className>com.parasoft.parabank.domain.logic.impl.BankManagerImpl</className> <methodName>requestLoan</methodName> <argumentTypes> <string></string> </argumentTypes> <mode>RECORD</mode> </method> </callingMethods> <virtualizedMethods> <method> <className>com.parasoft.parabank.domain.logic.LoanProvider</className> <methodName>requestLoan</methodName> <argumentTypes> <string></string> </argumentTypes> <mode>RECORD</mode> </method> </virtualizedMethods> </callVirtualizationClass>
- Run ParaBank and request loan amounts; repeat steps 4 through 7 from the previous exercise.
- Switch the modes under the callVirtualizationClass section to VIRTUALIZE.
- Run ParaBank again. Notice how it now uses Virtualize to emulate the method calls.