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!

Friday, January 26, 2007

Hibernate alternatives for mappedBy to a superclass property

I've been working on a JPA/Hibernate prototype of an application that was previously mapped using Toplink. So this is the "meet in the middle" where there is an existing domain model that must be mapped to an existing schema. In reality, the domain model is somewhat free to change as long as the public interface stays the same. And if push comes to shove, schema changes are possible (but undesirable).

While trying to map a relationship to a superclass, I assumed that O/R relationships can be inherited in a manner analogous to OO. So I naively assumed I could:

  • make the superclass an @Entity

  • use single table inheritance

  • use a @ManyToOne in the superclass (and then do a @OneToMany with a mappedBy= in the other side of the relationship)

  • set up a discriminator column on the superclass

  • provide discriminator values in the subclasses

and viola... other classes could then hold references to the sublclasses.

So the key assumption here is that mappedBy could simply reference the property of the subclass even though the property is actually in the superclass (as you can do in an OO sense). But I ran into problems trying this. Such as: javax.persistence.PersistenceException: org.hibernate.AnnotationException: mappedBy reference an unknown target entity property:. And no, it's not simply a field vs. accessor visibility issue. You can try different variations (including the sin of making it a public reference) and it will have no effect.

So this explores some of the options that were tried, and what the tradeoff's are...

In the examples that follow (the naive and wrong way):

  • ExternalContactAssignment is the subclass of ContactAssignment (via single table inheritence)

  • APLEntity is the class that has the @OneToMany to a subclass (ExternalContactAssignment)

  • ContactAssignment is the superclass that has the @ManyToOne back-reference to APLEntity

The code looked like this:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="internal_ind", discriminatorType = DiscriminatorType.STRING)
@Table(name="CONTACT_ASSIGNMENT")
public abstract class ContactAssignment {

// bidirectional
@ManyToOne
@JoinColumn(name="APL_ENTITY_SEQ_NUM", nullable=false)
private APLEntity aplEntity;
...

}

@Entity
@DiscriminatorValue("N")
public class ExternalContactAssignment extends
ContactAssignment {

// nothing relevant in this class
...
}


@Entity
@org.hibernate.annotations.Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="APL_ENTITY")
public abstract class APLEntity
implements ExternalContactAssignable {

@OneToMany(mappedBy="aplEntity")
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
@org.hibernate.annotations.Where(clause="internal_ind='N'")
private List externalContactAssignments;
...

}

Note: I'm stripping the code down to the bare minimum here (the real classes have lots of other stuff not relevant to the problem).

According to Emmanuel Bernard (Hibernate developer), it is semantically incorrect to assume this mapping structure to work as expected.

There are three approaches, which vary in

  • How much schema change you'll live with

    • Can you add new tables?

    • Can you add new columns?

  • Tradeoffs between relational integrity and OO-to-relational consistency

  • If you can live with superclass not being an @Entity

Here are the three approaches...

@MappedSuperclass with Column per Subclass

Approach:

  • ContactAssignment is mapped with an @MappedSuperclass instead of an @Entity.

  • ContactAssignment (superclass) maintains the references to APLEntity

  • Table per subclass model (new table(s) required). Discriminators cannot be used (they'll be ignored).

By using @MappedSuperclass, you lose the ability to have a relationship to the superclass. You also lose polymorphic queries when using straight JPA--although Hibernate queries will still be polymorphic.

I didn't pursue this as I'm trying to minimize schema change (an evaluation criteria for Hibernate). However, the mapping would look something like this:

@MappedSuperclass
public abstract class ContactAssignment {
...
@ManyToOne
@JoinColumn(name = "APL_ENTITY_SEQ_NUM", nullable = false)
public APLEntity getAplEntity() {
return aplEntity;
}

public void setAplEntity(APLEntity aplEntity) {
this.aplEntity = aplEntity;
}

}


@Entity
@Table(name="EXTERNAL_CONTACT_ASSIGNMENT") // class-specific
public class ExternalContactAssignment extends ContactAssignment {

// not much needed

}


@Entity
@org.hibernate.annotations.Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "APL_ENTITY")
public abstract class APLEntity {

@OneToMany(mappedBy="aplEntity")
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.ALL,
org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
@org.hibernate.annotations.Where(clause="internal_ind='N'")
private List externalContactAssignments;

...

}

Relationship Column per Subclass with Discriminator Column

Approach:

  • Single table model

  • ExternalContactAssignment (subclass) maintains the references to !APLEntity

  • Distinct relationship (FK) column used for each subclass

  • Still requires a discriminator column

  • Duplicative @Where and @DiscriminatorColumn's (bit of a wart).

This is Emmanuel Bernard's recommended approach, as being the most consistent between the object model and the relational model.

Consequences are that pure JPA queries are no longer polymorphic (but Hibernate queries still should be). You can no longer have a relationship to the superclass ContactAssignment. It feels less "OO" since you are forced to push the relationship down to the subclass(es)... I want OO considerations to drive this, not O/R mapping considerations (transparency!). On the relational side, things get ugly. Each subclass requires its own FK column out to the APL_ENTITY table. Although this is probably why Emmanuel says it's the most consistent, I don't think it's worth the price. XOR columns like that don't play well with referential integrity. A given row should only have one value populated no matter how many subclasses you have. It make it harder to query and index, and conceptually make the design harder to understand--and it only gets worse as you add more subclasses to the mix. It also doesn't make sense to me to have a discriminator column and still require multiple FK cols. Here it is:

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="internal_ind", discriminatorType = DiscriminatorType.STRING)
@Table(name="CAU_CONTACT_ASSIGNMENT") // Note: referencing a new modified table
public abstract class ContactAssignment {

public abstract void setAplEntity(APLEntity aplEntity);

public abstract APLEntity getAplEntity();
}


@Entity
@DiscriminatorValue("N")
public class ExternalContactAssignment extends
ContactAssignment {

// bidirectional
@ManyToOne
@JoinColumn(name="EXT_APL_ENTITY_SEQ_NUM")
private APLEntity aplEntity;

@Override
public APLEntity getAplEntity() {
return aplEntity;
}

@Override
public void setAplEntity(APLEntity aplEntity) {
this.aplEntity = aplEntity;
}
}


@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="APL_ENTITY")
public abstract class APLEntity
implements ExternalContactAssignable {


@OneToMany(mappedBy="aplEntity", cascade={CascadeType.ALL})
@org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
@org.hibernate.annotations.Where(clause="internal_ind='N'")
private List externalContactAssignments;

}

Unidirectional Read Only Back-Reference

If I understand this correctly, this is basically ignoring Hibernate's ability to manage a bidirection relationship, and mapping a unidirectional read-only back reference from ContactAssignment to APLEntity. I believe this is what is discussed in Section 6.4.3 of Java Persistence With Hibernate. Making the back reference read-only tells Hibernate not to do a duplicative update when a ContactAssignment changes an APLEntity reference. Approach:

  • ContactAssignment is mapped with an @Entity.

  • ContactAssignment (superclass) maintains the references to !APLEntity

  • Single table model

  • Still requires a discriminator column

  • Duplicative @Where and @DiscriminatorColumn's (bit of a wart).

Emmanuel describes this approach as making the data design weaker. I'm not sure exactly how that is (or maybe what it means), or what tradeoff's are implied, but it's certainly is closest to what I was looking for:

  • no schema change required --> so no loss of relational integrity possible

  • scales easily with additional subclasses

  • let's OO considerations drive domain model design

Here's what it looks like

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="internal_ind", discriminatorType = DiscriminatorType.STRING)
@Table(name="CONTACT_ASSIGNMENT")
public abstract class ContactAssignment {

@ManyToOne
@JoinColumn(name="APL_ENTITY_SEQ_NUM", nullable=false)
private APLEntity aplEntity;

...
}

@Entity
@DiscriminatorValue("N")
public class ExternalContactAssignment extends
ContactAssignment {

// not much needed
}


@Entity
@org.hibernate.annotations.Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="APL_ENTITY")
public abstract class APLEntity
implements ExternalContactAssignable, Authorizable {


@OneToMany(cascade={CascadeType.ALL})
@JoinColumn(name="APL_ENTITY_SEQ_NUM", insertable=false, updatable=false)
@org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
@org.hibernate.annotations.Where(clause="internal_ind='N'")
private List externalContactAssignments;

...
}
Anyway, so I'll be moving ahead with this last approach as it is closest to what I want.

Wednesday, January 10, 2007

OC4J Rmi Client: NoClassDefFoundError: javax/ejb/EJBHome

Trying to run an OC4J rmi client where the oc4jclient.jar was packaged with the project (no longer in the jdeveloper home directory) produced some vexing problems with the following error:
java.lang.NoClassDefFoundError: javax/ejb/EJBHome
at java.lang.Class.getDeclaredMethods0(Native Method)
...
despite having the ejb.jar file on the classpath. After much frustration, it turns out that oc4jclient.jar uses a Class-Path attribute in its manifest:
Class-Path: lib/ejb.jar lib/mail.jar lib/oc4j_orb.jar lib/orbbase.jar
lib/iiop_support.jar lib/jms.jar lib/jta.jar ../../lib/xmlparserv2.ja
r ../../opmn/lib/optic.jar ../../oracle/jlib/oraclepki.jar ../../jlib
/oraclepki.jar ../../oracle/jlib/ojpse.jar ../../jlib/ojpse.jar
The problem seems to be that although the application classloader does have ejb.jar on the classpath, the oc4jclient.jar gets loaded with a different classloader that can't see it, and expects the ejb.jar to be in a lib dir relative to itself. Turns out you can ignore all the other jar references, only ejb.jar is required (luckily). So, where ever you place the oc4jclient.jar, just create a lib dir in the same directory and drop the ejb.jar into it--then the problem should be solved.

Another bit of weirdness was that it wouldn't seem to work with jdk 1.5.0_06. We looked in the ext dir for anything that might be messing it up but didn't find anything obvious. It seems too bizarre to the the jdk version was also causing a problem, but simply switching to 1.5.0_02 would fix other classpath problems.