Tuesday, January 8, 2008

Hacking an WSRP portlet request using Groovy

I wanted to invoke a portlet without having to run it through a portal. I have a portlet currently deployed in Oracle Portal and figured it would be "fun" to try invoking it using Groovy--I didn't see any slam-dunk toolkits for this (I didn't look too hard). WSRP uses Soap, so the first step was firing up Wireshark to sniff a real request (sanitized to protect the innocent):

POST /MyPortlet/portlets/WSRPBaseService HTTP/1.1
Host: mycomputer:8080
Connection: keep-alive, TE
TE: trailers, deflate, gzip, compress
User-Agent: RPT-HTTPClient/0.3-3
Oracle-ECID: 86139989405,1
Cookie: JSESSIONID=80f4383422b8fbea0c8532804298a55ba92d09dfb9a6
SOAPAction: "urn:oasis:names:tc:wsrp:v1:getMarkup"
Accept-Encoding: gzip, x-gzip, compress, x-compress
Content-type: text/xml; charset="utf-8"
Content-length: 2165

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns0="urn:oasis:names:tc:wsrp:v1:types"><env:Body><ns0:getMarkup><ns0:registrationContext><ns0:registrationHandle>C:xxx.xx.xx.xx:-5fd1fa94:11740915845:-7ffe</ns0:registrationHandle></ns0:registrationContext><ns0:portletContext><ns0:portletHandle>E:0:xxx.xx.xx.xx:-5fd1fa94:11740915845:-7ffd</ns0:portletHandle></ns0:portletContext><ns0:runtimeContext><ns0:userAuthentication>wsrp:xxxxxxxx</ns0:userAuthentication><ns0:portletInstanceKey>1179_354032_1179_1_1</ns0:portletInstanceKey><ns0:namespacePrefix>__ns1179_354032_1179_1_1_</ns0:namespacePrefix></ns0:runtimeContext><ns0:userContext><ns0:userContextKey>mylogin</ns0:userContextKey></ns0:userContext><ns0:markupParams><ns0:secureClientCommunication>false</ns0:secureClientCommunication><ns0:locales>en-us</ns0:locales><ns0:mimeTypes>text/html</ns0:mimeTypes><ns0:mode>wsrp:view</ns0:mode><ns0:windowState>wsrp:normal</ns0:windowState><ns0:clientData><ns0:userAgent>Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) RPT-HTTPClient/0.3-3</ns0:userAgent><ns0:extensions><oracle:GenericExtension xmlns:oracle="http://xmlns.oracle.com/portal/wsrp/v1"><ns0:NamedString name="X-Oracle-Device.MaxDocSize"><ns0:value>0</ns0:value></ns0:NamedString><ns0:NamedString name="X-Oracle-Device.Class"><ns0:value>pcbrowser</ns0:value></ns0:NamedString><ns0:NamedString name="X-Oracle-Device.Secure"><ns0:value>false</ns0:value></ns0:NamedString><ns0:NamedString name="X-Oracle-Device.Orientation"><ns0:value>landscape</ns0:value></ns0:NamedString></oracle:GenericExtension></ns0:extensions></ns0:clientData><ns0:markupCharacterSets>utf-8</ns0:markupCharacterSets><ns0:validNewModes>wsrp:view</ns0:validNewModes><ns0:validNewWindowStates>wsrp:normal</ns0:validNewWindowStates><ns0:validNewWindowStates>wsrp:maximized</ns0:validNewWindowStates><ns0:validNewWindowStates>wsrp:minimized</ns0:validNewWindowStates></ns0:markupParams></ns0:getMarkup></env:Body></env:Envelope>



It would probably take a bit of trial and error to hack that for another portal/portlet, but who knows.

Putting this in groovy (stripping out the JSESSIONID):

def headerLines = """\
Host: mycomputer:8080
Connection: keep-alive, TE
TE: trailers, deflate, gzip, compress
User-Agent: RPT-HTTPClient/0.3-3
Oracle-ECID: 86139989405,1
SOAPAction: "urn:oasis:names:tc:wsrp:v1:getMarkup"
Accept-Encoding: gzip, x-gzip, compress, x-compress
Content-type: text/xml; charset="utf-8"
Content-length: 2165
"""

def postBody = """\
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns0="urn:oasis:names:tc:wsrp:v1:types"><env:Body><ns0:getMarkup><ns0:registrationContext><ns0:registrationHandle>C:xxx.xx.xx.xx:-5fd1fa94:11740915845:-7ffe</ns0:registrationHandle></ns0:registrationContext><ns0:portletContext><ns0:portletHandle>E:0:xxx.xx.xx.xx:-5fd1fa94:11740915845:-7ffd</ns0:portletHandle></ns0:portletContext><ns0:runtimeContext><ns0:userAuthentication>wsrp:xxxxxxxx</ns0:userAuthentication><ns0:portletInstanceKey>1179_354032_1179_1_1</ns0:portletInstanceKey><ns0:namespacePrefix>__ns1179_354032_1179_1_1_</ns0:namespacePrefix></ns0:runtimeContext><ns0:userContext><ns0:userContextKey>mylogin</ns0:userContextKey></ns0:userContext><ns0:markupParams><ns0:secureClientCommunication>false</ns0:secureClientCommunication><ns0:locales>en-us</ns0:locales><ns0:mimeTypes>text/html</ns0:mimeTypes><ns0:mode>wsrp:view</ns0:mode><ns0:windowState>wsrp:normal</ns0:windowState><ns0:clientData><ns0:userAgent>Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) RPT-HTTPClient/0.3-3</ns0:userAgent><ns0:extensions><oracle:GenericExtension xmlns:oracle="http://xmlns.oracle.com/portal/wsrp/v1"><ns0:NamedString name="X-Oracle-Device.MaxDocSize"><ns0:value>0</ns0:value></ns0:NamedString><ns0:NamedString name="X-Oracle-Device.Class"><ns0:value>pcbrowser</ns0:value></ns0:NamedString><ns0:NamedString name="X-Oracle-Device.Secure"><ns0:value>false</ns0:value></ns0:NamedString><ns0:NamedString name="X-Oracle-Device.Orientation"><ns0:value>landscape</ns0:value></ns0:NamedString></oracle:GenericExtension></ns0:extensions></ns0:clientData><ns0:markupCharacterSets>utf-8</ns0:markupCharacterSets><ns0:validNewModes>wsrp:view</ns0:validNewModes><ns0:validNewWindowStates>wsrp:normal</ns0:validNewWindowStates><ns0:validNewWindowStates>wsrp:maximized</ns0:validNewWindowStates><ns0:validNewWindowStates>wsrp:minimized</ns0:validNewWindowStates></ns0:markupParams></ns0:getMarkup></env:Body></env:Envelope>"""

def url = new URL("http://mycomputer:8080/MyPortlet/portlets/WSRPBaseService")
def conn = url.openConnection()
conn.doOutput = true

headerArray = headerLines.split("\n")
headerArray.each {
iColon = it.indexOf(":")
name = it[0..<iColon]
value = it[iColon+1..-1].trim()
println """Header $name = $value"""
conn.setRequestProperty(name, value)
}

conn.outputStream << postBody
conn.inputStream.eachLine { println it }


Running this produces the following output:

&lt;p class="portlet-font"&gt;Welcome, this is the view mode.&lt;/p&gt;
Current Date/Time is: 2008-01-08 12:20:36.0</ns0:markupString><ns0:locale>en</ns0:locale><ns0:requiresUrlRewriting>false</ns0:requiresUrlRewriting><ns0:cacheControl><ns0:expires>0</ns0:expires><ns0:userScope>wsrp:perUser</ns0:userScope></ns0:cacheControl><ns0:preferredTitle>ComboPortlet</ns0:preferredTitle></ns0:markupContext></ns0:getMarkupResponse></env:Body></env:Envelope>



If you have suggestions for a good library for this, or improvements to the groovy code, please comment.

Friday, September 21, 2007

Opening JBoss AS HTTP Traffic Up To More Than Localhost

JBoss by default binds only to 127.0.0.1  So to open it up, use:

run -b <your machine's IP address>

To really do it right: Secure JBoss wiki page

Blather: I went to show off my latest Seam, Ajax4jsf, Richfaces suggestionBox/autocomplete widget to a coworker on JBoss AS 4.2 and had problem when I tried run against anything but localhost. Even on my own machine, but using my full hostname, the browser behaved strangely, not even giving a decent status code or anything. I used the Firefox Tamper plugin (Tamper + Firebug + Web Tools = web developer nirvana [okay, I stole this tagline from somewhere else in my web travels]) to see that the request status was "pending". Perhaps that is how JBoss goes stealth against port sniffing. Anyway, I figured it was just a security setting based on the principal of requiring developers to have to conscientiously open up security. But after reading the fine manual, I still didn't find it. Turns out this is a very popular question in the forums though. And, it was actually in the readme.html (sadly I missed it there) under "Configuration Issues" for 4.2.0 GA (however, I stopped reading at 4.2.1 GA--seems it should be under 4.2.1 since it still affects it).

For those googling:
Unable to connect connection
Internet Explorer cannot display the webpage

Thursday, September 13, 2007

Turning on Hibernate Logging using Seam on JBoss 4.2

At some point, you're going to need to see the sql and the bind variables that hibernate is using. When running Seam on JBoss AS 4.2 and above, there are a couple things you need to do. First, add the following lines to <jboss home>/server/default/conf/jboss-log4j.xml

<category name="org.hibernate">  
<priority value="TRACE"/>
<appender-ref ref="HIBERNATE"/>
</category>


This tells log4j to start logging hibernate at a very high detail.
Next, make sure persistence-dev.xml has the hibernate.show_sql property set to true.

<persistence-unit name="AutoPropPass5">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/AutoPropPass5Datasource</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.show_sql" value="true"/> <============ HERE
<property name="jboss.entity.manager.factory.jndi.name" value="java:/AutoPropPass5EntityManagerFactory"/>
</properties>
</persistence-unit>


Then, add commons-logging.jar to the project's war lib directory and tweak build.xml to include this jar in the deployment:

<copy todir="${war.dir}/WEB-INF/lib">
<fileset dir="${lib.dir}">
<include name="ajax4jsf*.jar" />
<include name="richfaces*.jar" />
<include name="oscache*.jar" />
<include name="commons-digester-*.jar" />
<include name="commons-beanutils-*.jar" />
HERE ============> <include name="commons-logging*.jar" />
<include name="jsf-facelets.jar" />
<include name="jboss-seam-*.jar" />
<exclude name="jboss-seam-gen.jar" />
</fileset>
</copy>


When this is all in place, you should be able to see output like the following in <jboss home>/server/default/log/server.log:

2007-09-13 09:34:55,854 TRACE [org.hibernate.type.StringType] binding 'updi' to parameter: 1
2007-09-13 09:34:56,823 DEBUG [org.hibernate.jdbc.AbstractBatcher] about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
2007-09-13 09:34:56,823 TRACE [org.hibernate.jdbc.AbstractBatcher] closing statement
2007-09-13 09:34:56,823 DEBUG [org.hibernate.jdbc.ConnectionManager] aggressively releasing JDBC connection
2007-09-13 09:34:56,823 DEBUG [org.hibernate.jdbc.ConnectionManager] releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
2007-09-13 09:34:56,823 DEBUG [org.hibernate.util.JDBCExceptionReporter] could not execute query [select pass0_.PASS_ID as PASS1_35_, pass0_.ADDRESS as ADDRESS35_, pass0_.state as state35_, pass0_.VERSION as VERSION35_, pass0_.reason as reason35_, pass0_.status as status35_, pass0_.DESTINATION as DESTINAT7_35_, pass0_.INS_UPD_ID as INS8_35_, pass0_.INS_UPD_TS as INS9_35_, pass0_.PASS_HOLDER_PID as PASS17_35_, pass0_.PASS_CREATOR_PID as PASS18_35_, pass0_.HOLDER_GROUP_ID as HOLDER10_35_, pass0_.HOLDER_GROUP_SUP_PID as HOLDER11_35_, pass0_.CHECKOUT_DATE as CHECKOUT12_35_, pass0_.REMOVE_DATE as REMOVE13_35_, pass0_.DUE_DATE as DUE14_35_, pass0_.ADDRESS2 as ADDRESS15_35_, pass0_.CITY as CITY35_ from ERMPMGR.PP_PASS pass0_, ERMPMGR.PP_PERSON_MV person1_ where pass0_.PASS_HOLDER_PID=person1_.PERSON_ID and (upper(person1_.FULL_NAME) like upper(?+'%'))]
java.sql.SQLException: ORA-01722: invalid number

at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:124)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:304)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:271)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:625)
...


Notes:

  • Older versions of JBoss used a different file than jboss-log4j.xml (perhaps log4j.xml) and perhaps put a log4j.properties in the META-INF dir.

  • TRACE is used instead of DEBUG which worked in prior versions.

  • I'm not positive the {hibernate.show_sql bit is required, but seam-gen puts it in there by default.


In case your wondering why I was getting the "invalid number" error, it's because Oracle uses the || symbol for string concatenation, not +. When I fixed that, it started working.

Saturday, May 19, 2007

Eclipse RCP missing the Business Desktop opportunity?

I was recently involved (tangentially) with a biz app proof-of-concept effort that used an eclipse RCP front-end for a J2EE middle tier. The prototype was quite successful. However, when considering adopting the RCP approach, there’s concern that if we just start knocking off a bunch of apps as plug-ins, we’ll run into some problems:

  • duplication (code, functionality)
  • managing relationships/dependencies between plug-in
  • biz users complaining about the update mechanism

I’m willing to bet there are others I’m not thinking of. Not to suggest that these are problems with RCP or Eclipse--I’m sure a team of seasoned plug-in developers could produce and maintain an excellent suite of custom RCP biz apps. But after doing a few such plug-ins, they’d probably realize that they could refactor out a foundational plug-in that provides common support services that were duplicated among the various app plug-in. In fact, this concept already has a name… the “Business Desktop”. I heard this term from a valtech-tv.com presentation titled "Enterprise applications with Rich iDesktop applications on Eclipse" (see link below). Essentially, a Business Desktop would be an RCP-based business application environment. The Business Desktop could provide services including:

  • provisioning
  • search
  • authentication/authorization
  • preferences
  • Expression Language (assuming contexts are set up)
  • scripting
  • logging
  • error handling
  • service locator

The overarching theme is something like “app plug-ins do biz logic, the business desktop does everything else”. Such an approach would greatly reduce the learning curve for RCP adoption for business apps.

There are already some pieces of this available too. The Maya project is working the provisioning piece. And I’m sure others are out there (or are available via the Eclipse api but could be wrappered for convenient plug-in use). But I don’t see anything like the Business Desktop on the horizon. Is it there but I am missing it? Is it not needed? Comments welcome.

References:

Enterprise applications with Rich iDesktop applications on Eclipse:
http://www.valtech-tv.com/
I can’t give a direct link to the "" since it is flash based. Registration required unfortunately--but there are some good technical screencasts there:

Maya project:
http://www.eclipse.org/proposals/maya/

Friday, May 4, 2007

Using seam-gen.reveng.xml to control tables used for entity generation

I was having problems using seam-gen to try out reverse engineering on an Oracle Express database. Seam-gen was finding all sorts of objects somehow exposed to the user, rather than the DEMO_ tables I was trying to generate off of. Turns out that a feature (undocumented?) was added in 1.2.1.GA that let's you use a reveng.xml file to control this. After you run:

C:\temp\jboss-seam-1.2.1.GA>seam new-project

You can go to the \resources\seam-gen.reveng.xml

And add a line such as this:
<table-filter match-schema="MYSCHEMA" match-name="DEMO.*" exclude="false"/>

This should effectively restrict the tables as desired. Note you can use multiple of these entries to "carve out" the set of tables you want (although I'm not sure what the exact semantics are of using various combinations of exclude=true/false for different entries).

Some things to watch out for:
  • Case sensitivity
    At least for Oracle, you need to use upper case (I suppose you could attemp some kind of case insensitive regex).
  • Owning schema in seam-gen.reveng.xml
    Seems like you have to specify the owning schema for the tables, even if you are connecting with a user that has privileges for the tables.
  • Owning schema in build.properties
    You need to specify connection properties to the owning schema, at least temporarily, or you may get WARNING: Exception while trying to get indexinfo on...
I haven't tried enough variations to prove all of the above bullets, but if you're experiencing problems, try out some of these suggestions.

Reference for table-filter

Wednesday, March 7, 2007

junit ant task problems in eclipse

So your junit ant task won't run, failing with classpath problems. How hard can that be to figure out? It sure stumped me for a while.

You figure you need ant-junit.jar on the classpath. Hmmm, is that the project classpath (a lib dir maybe)? Or the ant home dir? But wait, it's already there... so what's the problem.

The problem is that ant-junit.jar is not self-contained, as I naively assumed. You need to supply the junit.jar file to ant, which is referenced by ant-junit.jar. Make sense when you think about it--but in the haze of red error messages whizzing by, it's easy to miss. Anyway, it's easy to fix, as documented in this blog post.

Thanks Ryan!

Wednesday, February 14, 2007

Remote debugging in JBoss

I recently had the need to fire up remote debugging in JBoss. I'm using Eclipse, but I'm sure the directions are fairly similar for any remote debug-capable IDE. Pretty much you can follow the directions here, with one change (for windows) and one suggestion:

Change: The directions suggest that for windows, you need to use the shared memory transport flag: transport=dt_shmem. However, that didn't work for me. But the original socket flat, recommended for nix, did work.

Suggestion: Rather than copy the run file and put in these parameters, it's pretty easy to set them in the environment and then just call run (and avoids copied code). Here's mine:

set JAVA_OPTS=-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=4142,suspend=n
call run.bat

Note that you don't need double quotes around the env var, despite any spaces. However, you cannot have any spaces between JAVA_OPTS, the =, and the var definition.

Happy debugging!