Showing posts with label byteman. Show all posts
Showing posts with label byteman. Show all posts

Sunday, March 10, 2013

Learning Switchyard, from the inside out

My job with Red Hat is exciting.  I'm a maintenance engineer on the SOA (and sometimes BRMS) products, both of which are constantly growing and introducing new features.  Keeping up can be quite a task.

The next big change for SOA is going to be the introduction of the new 'Switchyard' infrastructure.  Learning to use Switchyard is one thing-- there are lots of videos, blogs, etc. for that.  But how do you learn how it really works?

This question is one I often describe to people with an analogy.  Using software is like learning to drive a car.  Supporting software is a superset of that-- it's more like having to know what makes the engine, transmission, etc. work.  It's impossible to help people solve their software problems without understanding what makes it all tick.

So I'm trying to learn about the innards of Switchyard.  I'll share an important technique today, one that can be re-used with different applications.  I hope you find it helpful.

First, let's decide on a use case to investigate.  Switchyard has a nice 'quickstarts' directory that you can use to see how Switchyard apps are built.  For today, I'm interested in how Switchyard transforms data so I thought I'd work with the transform-jaxb example.

This quickstart exposes a service through SOAP.  You just use maven ('mvn clean install') to package the project, deploy it to Switchyard, then invoke the application with SOAPUI or another SOAP client.  (Hint:  Sometimes you may wonder which URL to use in situations like this.  The console may not always tell you.  If you grep the log for 'wsdl' you'll often find one you can use.)

So now that we've 'learned to drive the car' (deployed an application, tested it') let's 'learn how the car works'.

STEP1:  Look over the source, pick a class that is likely to be invoked.  In this case, there is a bean used to represent the Order we input to SOAPUI.  I'm guessing the getters will need to be invoked on this class as the SOAP is marsheled on input.

STEP2:  Let's write a Byteman rule to give us a stack trace when this method is invoked.  (If you don't know about Byteman, please go find out ASAP!  Every Java programmer should know about Byteman....)
Here's the rule I cooked up:

RULE Learn about transformation
CLASS org.switchyard.quickstarts.transform.jaxb.Order
METHOD getOrderId
AT ENTRY
IF TRUE
DO traceStack("CALLED ORDER GETTER " + "\n", 100)
ENDRULE

STEP3: Deploy that rule (again, please be familiar with Byteman-- install Byteman into your app server, submit this rule).

STEP4:  Re-run your SOAPUI test.  Then go to your server.log for the Byteman output.  Looking over the output (included below)  we get a wealth of information.  Just in one quick pass, here's some of what we can gather: 

- This work is being done on an http thread (http--127.0.0.1-8080-1)
- CXF is handling the web-servicey stuff
- There's a Switchyard 'ExchangeImpl' being used.  (We should expect this!)
- Apache Camel is involved early on.  Camel users will recognize the 'Producer'

From here, I'll look for other classes and methods to use in Byteman rules to learn about how Switchyard is going about it's work.  Since we're working with high-quality open source software, we can also look at the classes and methods cited in this stacktrace. 

A stack trace like this (or better yet, a few of them taken off the same user use-case) can tell us a lot about what's going on under the hood.

Happy learning!


:19,729 INFO  [org.jboss.as.server] (DeploymentScanner-threads - 2) JBAS018559: Deployed "switchyard-quickstart-transform-jaxb.jar"
14:38:51,432 INFO  [stdout] (http--127.0.0.1-8080-1) CALLED ORDER GETTER
14:38:51,433 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.quickstarts.transform.jaxb.Order.getOrderId(Order.java:-1)
14:38:51,434 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.quickstarts.transform.jaxb.OrderServiceBean.submitOrder(OrderServiceBean.java:31)
14:38:51,434 INFO  [stdout] (http--127.0.0.1-8080-1) sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
14:38:51,435 INFO  [stdout] (http--127.0.0.1-8080-1) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
14:38:51,436 INFO  [stdout] (http--127.0.0.1-8080-1) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
14:38:51,436 INFO  [stdout] (http--127.0.0.1-8080-1) java.lang.reflect.Method.invoke(Method.java:616)
14:38:51,436 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.component.bean.ServiceProxyHandler.handle(ServiceProxyHandler.java:130)
14:38:51,437 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.component.bean.ServiceProxyHandler.handleMessage(ServiceProxyHandler.java:89)
14:38:51,437 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.bus.camel.processors.ProviderProcessor.process(ProviderProcessor.java:39)
14:38:51,438 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
14:38:51,438 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,439 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
14:38:51,439 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,440 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,440 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
14:38:51,441 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,441 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:73)
14:38:51,441 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,442 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
14:38:51,442 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,443 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.interceptor.TraceInterceptor.process(TraceInterceptor.java:91)
14:38:51,443 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,444 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
14:38:51,444 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,445 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.bus.camel.audit.FaultProcessor.process(FaultProcessor.java:52)
14:38:51,446 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,446 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.RedeliveryErrorHandler.processErrorHandler(RedeliveryErrorHandler.java:330)
14:38:51,447 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:220)
14:38:51,447 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.RouteContextProcessor.processNext(RouteContextProcessor.java:45)
14:38:51,448 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,449 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.interceptor.DefaultChannel.process(DefaultChannel.java:303)
14:38:51,449 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,450 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.Pipeline.process(Pipeline.java:117)
14:38:51,450 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
14:38:51,451 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.RouteContextProcessor.processNext(RouteContextProcessor.java:45)
14:38:51,451 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,452 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.UnitOfWorkProcessor.process(UnitOfWorkProcessor.java:122)
14:38:51,453 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.RouteInflightRepositoryProcessor.processNext(RouteInflightRepositoryProcessor.java:48)
14:38:51,453 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,454 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,454 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.processNext(DelegateAsyncProcessor.java:99)
14:38:51,455 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:90)
14:38:51,456 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:73)
14:38:51,456 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
14:38:51,457 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:61)
14:38:51,457 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.UnitOfWorkProcessor.processAsync(UnitOfWorkProcessor.java:150)
14:38:51,458 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.UnitOfWorkProcessor.process(UnitOfWorkProcessor.java:117)
14:38:51,459 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:99)
14:38:51,459 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.DelegateAsyncProcessor.process(DelegateAsyncProcessor.java:86)
14:38:51,460 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.processor.UnitOfWorkProducer.process(UnitOfWorkProducer.java:63)
14:38:51,460 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:360)
14:38:51,461 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.impl.ProducerCache$2.doInProducer(ProducerCache.java:331)
14:38:51,461 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.impl.ProducerCache.doInProducer(ProducerCache.java:227)
14:38:51,462 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.impl.ProducerCache.sendExchange(ProducerCache.java:331)
14:38:51,462 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.impl.ProducerCache.send(ProducerCache.java:169)
14:38:51,463 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.impl.DefaultProducerTemplate.send(DefaultProducerTemplate.java:111)
14:38:51,464 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.camel.impl.DefaultProducerTemplate.send(DefaultProducerTemplate.java:97)
14:38:51,464 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.bus.camel.ExchangeDispatcher.dispatch(ExchangeDispatcher.java:64)
14:38:51,465 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.internal.ExchangeImpl.sendInternal(ExchangeImpl.java:203)
14:38:51,467 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.internal.ExchangeImpl.send(ExchangeImpl.java:114)
14:38:51,468 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.component.soap.InboundHandler.invoke(InboundHandler.java:213)
14:38:51,470 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.component.soap.endpoint.BaseWebService.invoke(BaseWebService.java:113)
14:38:51,470 INFO  [stdout] (http--127.0.0.1-8080-1) org.switchyard.component.soap.endpoint.BaseWebService.invoke(BaseWebService.java:43)
14:38:51,470 INFO  [stdout] (http--127.0.0.1-8080-1) sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
14:38:51,472 INFO  [stdout] (http--127.0.0.1-8080-1) sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
14:38:51,473 INFO  [stdout] (http--127.0.0.1-8080-1) sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
14:38:51,474 INFO  [stdout] (http--127.0.0.1-8080-1) java.lang.reflect.Method.invoke(Method.java:616)
14:38:51,474 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.ws.common.invocation.AbstractInvocationHandlerJSE.invoke(AbstractInvocationHandlerJSE.java:111)
14:38:51,475 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.wsf.stack.cxf.JBossWSInvoker._invokeInternal(JBossWSInvoker.java:181)
14:38:51,475 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.wsf.stack.cxf.JBossWSInvoker.invoke(JBossWSInvoker.java:127)
14:38:51,476 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58)
14:38:51,476 INFO  [stdout] (http--127.0.0.1-8080-1) java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
14:38:51,477 INFO  [stdout] (http--127.0.0.1-8080-1) java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
14:38:51,477 INFO  [stdout] (http--127.0.0.1-8080-1) java.util.concurrent.FutureTask.run(FutureTask.java:166)
14:38:51,478 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.workqueue.SynchronousExecutor.execute(SynchronousExecutor.java:37)
14:38:51,478 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:106)
14:38:51,479 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
14:38:51,480 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
14:38:51,480 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:207)
14:38:51,481 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.wsf.stack.cxf.RequestHandlerImpl.handleHttpRequest(RequestHandlerImpl.java:91)
14:38:51,481 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.wsf.stack.cxf.transport.ServletHelper.callRequestHandler(ServletHelper.java:169)
14:38:51,482 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.wsf.stack.cxf.CXFServletExt.invoke(CXFServletExt.java:87)
14:38:51,482 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:185)
14:38:51,483 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:108)
14:38:51,484 INFO  [stdout] (http--127.0.0.1-8080-1) javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
14:38:51,484 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.wsf.stack.cxf.CXFServletExt.service(CXFServletExt.java:135)
14:38:51,484 INFO  [stdout] (http--127.0.0.1-8080-1) org.jboss.wsf.spi.deployment.WSFServlet.service(WSFServlet.java:140)
14:38:51,485 INFO  [stdout] (http--127.0.0.1-8080-1) javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
14:38:51,485 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329)
14:38:51,486 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248)
14:38:51,487 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
14:38:51,487 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161)
14:38:51,488 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
14:38:51,488 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
14:38:51,489 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
14:38:51,489 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
14:38:51,490 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
14:38:51,491 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
14:38:51,491 INFO  [stdout] (http--127.0.0.1-8080-1) org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
14:38:51,492 INFO  [stdout] (http--127.0.0.1-8080-1) java.lang.Thread.run(Thread.java:679)

Saturday, February 11, 2012

Powerful Java Hacking made easy - use good judgement!


A very powerful hack - use with caution!

Here's a very quick tutorial on how you can use Byteman to change a running application on the fly. You can insert arbitrary code into a running application server. Caveat CodeMonkeytor!

Let's say we have a JEE 6 application that's misbehaving. You can easily attach Byteman to the app server, then inject the code you want-- without bringing down the server!

I like to use 2 scripts with Byteman, one to attach to the server (since this should be done only once), and one to check and install the Byteman rules I want to run. This way, you can incrementally add to your Byteman rules.

There's an example of a misbehaving Servlet at the bottom of this post. It's bad code. Let's pick on a single fault. The user is asked to provide two numbers, the servlet is supposed to divide them and provide the result. But if the user enters a zero for the second argument, a divide by zero exception results! Please install the servlet and try for yourself.

Making JEE apps is trivial now that JEE 6 is here. I'd recommend using JBoss AS 7, and building with Maven. (I used to hate Maven, but since I've started using it, I hate it a little less. Still, it seems better than Ant in easy cases.) So please find the Maven pom.xml at the bottom of this post, with the Servlet code.

But this is a post about Byteman, so I'm going to put that code first. Here are the scripts:

# InstallByteman.sh - run this only once, after JBoss is already running. It doesn't matter if JBoss has been running 7 months or 7 seconds.

# There should be 4 lines to this script-- in case your browser line-wraps.....
#!/bin/bash
export JBOSS_HOME=/home/rick/Tools/JBoss/AS7/jboss-as-7.1.0.Beta1b
export BYTEMAN_HOME=/home/rick/Tools/Byteman/byteman-2.0
export BYTEMAN_BIN=$BYTEMAN_HOME/bin
$BYTEMAN_BIN/bminstall.sh -b -Dorg.jboss.byteman.transform.all $JBOSS_HOME/jboss-modules.jar

# InstallRules.sh - This script validates and installs your Rules. You can run this as many times as you need, to incrementally build your rules up.

# There should be 10 lines to this script-- in case your browser line-wraps.....

#!/bin/bash
export JBOSS_HOME=/home/rick/Tools/JBoss/AS7/jboss-as-7.1.0.Beta1b
export BYTEMAN_HOME=/home/rick/Tools/Byteman/byteman-2.0
export BYTEMAN_BIN=$BYTEMAN_HOME/bin
# Export this so Byteman can validate your classes. This is wherever you have compiled your classes to.
export APP_TGT=/home/rick/Blog_Temp/Byteman2/simpleappservlet30/target/classes
# check it
$BYTEMAN_BIN/bmcheck.sh -cp $APP_TGT/HackFix.btm
# add the rule
$BYTEMAN_BIN/bmsubmit.sh HackFix.btm

So we can see in the above Install script that we have a Byteman rule named 'HackFix.btm'. Here it is.

(BTW, what this script is doing is changing the second argument passed to the method 'makeDivision' into a 1 if it is a zero. To prevent divide-by-zero...)

RULE Hack Fix for Divide Servlet
CLASS com.flyingdog.SimpleServlet
METHOD makeDivision
AT ENTRY
IF 0 == $2
DO $2 = 1;
ENDRULE

--------------------------------------------------------------------------------------------

So that's all the Byteman stuff above. You can use those samples to attach to a running application server (ANY application server, not just JBoss) and inject whatever arbitrary code you want. Cool (and dangerous), huh?




Ok, if you're like me, you'd love a quick way to try that out. So here's the pom.xml for the SimpleServlet...

 <project xmlns="http://maven.apache.org/POM/4.0.0"  
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.flyingdog</groupId>
<artifactId>notsogoodapp</artifactId>
<packaging>war</packaging>
<version>1.0</version>
<name>notsogoodapp</name>
<url>http://maven.apache.org</url>
<build>
<finalName>notsogoodapp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1-beta-1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>java.net</id>
<url>http://download.java.net/maven/2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

Now the bad Servlet.. Install it, have it divide a few numbers. Especially give it a zero to divide by, and notice the ugly blowup! Then use the above Byteman script to 'fix' the problem.


 package com.flyingdog;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.StringBuffer;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = {"/simpleservlet", "*.foo"})
public class SimpleServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
try {
response.setContentType("text/html");
PrintWriter printWriter = response.getWriter();
printWriter.println("<h2>");
printWriter.println("</h2>");
// if there is a value there, try to provide an answer
if (null != request.getParameter("firstValue")){
printWriter.println(makeAnswer(request));
}
// print the form that requests numbers
printWriter.println(makeForm());
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
private String makeAnswer(HttpServletRequest request){
String first = request.getParameter("firstValue");
String second = request.getParameter("secondValue");
int iFirst = Integer.parseInt(first);
int iSecond = Integer.parseInt(second);
int answer = makeDivision(iFirst, iSecond);
String response = first + " divided by " + second + " equals " + answer;
return response;
}
private int makeDivision(int first, int second){
return first / second;
}
private String makeForm(){
StringBuffer sb = new StringBuffer();
sb.append("<form method=\"post\" action=\"simpleservlet\">\n");
sb.append("<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n");
sb.append(" <tr>\n");
sb.append(" <td>Please enter two numbers to divide:</td>\n");
sb.append(" <td><input type=\"text\" name=\"firstValue\" /></td>\n");
sb.append(" <td><input type=\"text\" name=\"secondValue\" /></td>\n");
sb.append(" </tr>\n");
sb.append(" <tr>\n");
sb.append(" <td></td>\n");
sb.append(" <td><input type=\"submit\" value=\"Submit\"></td>\n");
sb.append(" </tr>\n");
sb.append("</table>\n");
sb.append("</form>\n");
return sb.toString();
}
}


So there you have it. To recap:
  1. Use the pom.xml and the Servlet code to make the .war
  2. Deploy the .war to a JEE 6 app server, like JBoss AS 7.
  3. Access the servlet at http://localhost:8080/notsogoodapp/simpleservlet
  4. Observe how it divides two numbers. Let it try to divide by zero, see blowup.
  5. Run the Byteman scripts, see how blowup stops.

Happy Hacking!