All
|
Diving
|
Java
|
General
|
Databases
|
Unix
Wednesday February 01, 2006
High Performance Message Processing with BMT Message-Driven Beans and Spring
Article High Performance Message Processing with BMT Message-Driven Beans and Spring I wrote together with Eugene has been published today on BEA dev2dev.
We're discussing arhitecture of non-transactional messages consumption with XA-like 'one-and-only-one' quality of service but without overhead of distributed transaction (in most cases).
Your comments are always welcome.
( Feb 01 2006, 09:59:08 AM EST )
Permalink
Sunday January 22, 2006
Passing data between advices in Spring
Spring version 1.2.6 added very useful new method getUserAttributes()
into ReflectiveMethodInvocation class (Spring's implementation of AOP Alliance MethodInvocation interface) which provides invocation-bound alternative to a ThreadLocal and allows passing data between advices in call chain.
I don't want to argue with purists who say that each advice should be totally independent from each other. In real world application it's almost always beneficial to be able to pass some data between interceptors either because it's expensive to recreate every time or because data computed in one interceptor should be accessible to next interceptor(s) in chain. Spring's implementation uses lazy initialized Map and therefore there is zero performance impact if your code is not calling this method.
public class AdviceA implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("AdviceA");
ReflectiveMethodInvocation mi = (ReflectiveMethodInvocation)invocation;
String data = "Now is "+new Date();
mi.getUserAttributes().put("data", data);
return invocation.proceed();
}
...
// another advice somewhere down the call chain:
public class AdviceB implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
ReflectiveMethodInvocation mi = (ReflectiveMethodInvocation)invocation;
String data = (String)mi.getUserAttributes().get("data");
System.out.println("AdviceB :"+data);
return invocation.proceed();
}
( Jan 22 2006, 10:02:38 PM EST )
Permalink
Comments [0]
Wednesday November 16, 2005
Peak performance tuning of CMP Entity beans
My article about CMP Beans performance tuning has been published on BEA dev2dev.
Although not as sexy as AOP on Rails ;-) Entity beans still used widely and amazingly not all developers ultimately familiar or even heard about many advanced features available in any leading J2EE server. This article tries to cover different concurrency strategies and gives some recommendation how and when to use it. Your comments are welcome.
( Nov 16 2005, 10:18:15 AM EST )
Permalink
Comments [5]
Wednesday November 02, 2005
And free Oracle for all...
I've been skeptical about new versions of PostgreSql and MySql with enterprise features support, but I guess competition is a good thing after all as Oracle just released 10g Express Edition (XE) (beta) for Windows and Linux, which is free to develop, deploy, and distribute.
At quick glance all features of 10g release 2 supported in XE, including Oracle Text for full text indexing. Maximum DB size is limited by 4Gb and XE will use only one CPU. I guess it's fair limitations for entry level, pilot and even for many medium-sized applications.
Nice move, Oracle. Why would I want to use MySql now? ;-)
( Nov 02 2005, 12:41:42 PM EST )
Permalink
Comments [2]
Wednesday October 26, 2005
MySQL 5 and support for XA transactions
MySQL 5 RC had been released recently with lots of new major features.
Among the others is support for XA transactions announced as well.
Although it's good to see that open source DBMSs getting closer feature wise to major databases, one should be careful when considering throwing away his Oracle and migrating to MySQL. I recently posted
my thoughts about it in dev2dev blog and Jason Carreira also shared his opinion on the state of open source transaction monitors.
So let's take a look at some limitations of current 'support' of XA transactions in MySQL 5 RC. For example:
- For XA START, the JOIN and RESUME clauses are not supported
- For XA END the SUSPEND [FOR MIGRATE] clause is not supported
So what does it mean and what impact it could have on your J2EE application? Well, if you're using Session Beans (or Spring's JTATransactionManager wrapper for that matter) you better be careful with Not Supported and Requires New transaction attributes. J2EE server should execute XAResource.end(xid, TM_SUSPEND) on all resources which was enlisted in transaction which is being suspended and if it's not clear how it's going to work in MySQL. To prevent some comments, sure 'there are workarounds': for example JDBC driver implementation could create new connection to the database to handle work done in new transaction without actually 'suspending' previous transaction in MySQL, but this would lead to extra connections and complexity in JDBC driver code (== more bugs). Again it's interesting question what would happen if something (DB, Tx monitor or client) would crash in that time, will they be able to recover? Af far as I know, XAResource doesn't need to persist information about XA transaction state before prepare or suspend is called.
But may be we should not be worrying about scenario described above because of the following 'implementation detail' in MySQL XA support:
If an XA transaction has reached the PREPARED state and the MySQL server dies, the transaction can be continued after the server restarts. That is as it should be. However, if the client connection terminates and the server continues to run, the server rolls back any pending XA transaction, even one that has reached the PREPARED state.
Are you ready for heuristic outcomes?
( Oct 26 2005, 03:50:49 AM EDT )
Permalink
Wednesday October 19, 2005
JMS messaging with Spring...nice but there is no magic
Some gotchas and things to keep in mind when using Spring's JMS support in J2EE server. Read details in my post on dev2dev.
( Oct 19 2005, 01:02:26 PM EDT )
Permalink
Friday September 23, 2005
Google toolbar - what a shame!
I wanted to try the release of google toolbar today, but apparently it doesn't work in Mozilla Deer Park (beta of Firefox 1.5). Although they claim that ...For the extension to work, you must be using version 1.0 or later.
This is a shame guys, don't you have like extra couple billions dollars to spend? After all, most of the extensions already supports Firefox 1.5, including CruiseControl Monitor. ;-)
( Sep 23 2005, 02:51:01 PM EDT )
Permalink
Monday September 19, 2005
CruiseControl Monitor extension with JMX actions support
New version of
CruiseControl Monitor has been released
with major functionality update - now JMX actions could be invoked from the Extension's popup menu on any of the project. Of course it works only for Java version of CruiseControl started with JMX console enabled. User should specify URL for JMX console . Projects build statuses still parsed from status page HTML because it's not clear for now how (if possible at all) to get statuses for all projects deployed on CruiseControl server in one HTTP request to JMX. If anybody know how to do that please drop me a line, I'd be interested to learn it. If it's possible, then parsing HTML could be dropped completely.
Deer Park beta (Firefox 1.4) also supported starting from this version.
Technorati Tags:
Java
Javascript
Mozilla
Firefox
CruiseControl
( Sep 19 2005, 04:33:12 PM EDT )
Permalink
Friday September 16, 2005
CruiseControl Monitor plugin for Firefox
Inspired by recent widget for OS X I wrote similar plugin for Firefox. See screenshots and install plugin from here.
It's draft version, basically couple hours of development so no advanced functionality just yet ;-). Feel free to post your comments/suggestions.
( Sep 16 2005, 04:01:45 PM EDT )
Permalink
Wednesday August 31, 2005
If it's not broken, why it doesn't work?
Billy Newport quickly responded on my post about broken JTA functionality in Websphere. It's certainly good to hear the voice from IBM trenches, but I see some flaws in Billy's response.
First of all, as a user I don't care what kind of 'v-object' getting passed around, all I need is explainable/predictable behaviour. Nothing in the JTA or EJB/J2EE spec prohibits passing XA-aware resource across Tx boundaries, moreover it works just fine in plain JTA as had been shown previously. I still don't understand why is it logical to expect it to work differently in J2EE container which supports the very same JTA spec?
Billy goes into some implementation details here:
The vconnection is associated with a physical one in the context of T1 in BeanA. When you pass the vconnection to the second B which has a new transaction then the app server allocates a new physical connection for that transaction.
I say fine, why than inserts which made in this 'new physical connection for new transaction' is not getting rolled back when we rolling back transaction B?
( Aug 31 2005, 03:46:26 PM EDT )
Permalink
Is JTA support broken in most J2EE servers?
As I have demonstrated in my previous post it's perfectly legal from JTA point of view to share XAResource across XA transactions boundaries, and it works properly when executed manually, at least with Oracle 9i server. Now it's a time to try how different J2EE servers are handling same scenario.
My test setup is pretty simple: create XA JDBC Connection pool in J2EE server and deploy two Local Stateless session beans (SLSB), BeanA and BeanB, both with 'RequiresNew' CMT setting.
Execute following use case (slightly artificial, I agree. In real life I doubt anyone would have to do that but nevertheless)
- BeanA:
- open JDBC connection from XA Datasource
- insert record 1 into DB
- call BeanB passing Connection as parameter
- BeanB:
- insert record 2 into DB
- call context.setRollbackOnly()
What do you think is expected behaviour should be? I'd say one record
should be in database (from BeanA) but not both of them because second transaction was marked for rollback.
I run this example under WebLogic 8,
WebSphere 6 and JBoss 4 so far, and only WebLogic seems to handle this case
correctly. On the other hand, JBoss developers seems to believe that this is correct behaviour to have both records in the database, but for me it's not what developers would expect given the spirit of JTA specification. What's your opinion?
Technorati Tags:
Java
JTA
XA
Transactions
Oracle
WebLogic
WebSphere
JBoss
( Aug 31 2005, 11:36:25 AM EDT )
Permalink
Thursday August 18, 2005
Sharing JDBC Connection across JTA transactions
Surprisingly not many developers aware that according to a current JTA 1.0.1 spec (part of J2EE 1.3) it's perfectly legal to use the same transactional resource across multiple transactions (see section 3.4.6 of the spec). It's responsibility of TransactionManager (== J2EE application server) to ensure that only one transaction is enlisted with the resource at any given time.
To demonstrate this behaviour, let's take for example, code from my previous post and modify it to emulate correct TransactionManager behaviour. We would suspend first transaction and then start another XA transaction (with different Xid)
...
XADataSource xaDs = ...;
XAConnection xaConn = xaDs.getXAConnection();
XAResource xaResource = xaConn.getXAResource();
Xid xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x01});
// start tx1
xaResource.start(xid1, XAResource.TMNOFLAGS);
Connection conn = xaConn.getConnection();
Statement st = conn.createStatement();
// execute SQL in tx1
st.executeUpdate("insert into a(a_name) values('test 1'");
// suspend tx1 (note TMSUSPEND flag)
xaResource.end(xid1, XAResource.TMSUSPEND);
// start second tx with different Xid
Xid xid2 = new MyXid(100, new byte[]{0x02}, new byte[]{0x02});
xaResource.start(xid2, XAResource.TMNOFLAGS);
// we can re-use the same statement
// but the call would execute in tx2
st.executeUpdate("insert into a(a_name) values('test 2'");
// commit tx2
xaResource.end(xid2, XAResource.TMSUCCESS);
int result2 = xaResource.prepare(xid2);
if (result2 != XAResource.XA_OK) {
throw new RuntimeException("tx2 prepare returns unsupported code: "+result2);
}
xaResource.commit(xid2, false);
// resume tx1 (note TMRESUME flag)
xaResource.start(xid1, XAResource.TMRESUME);
st.close();
conn.close();
// rollback tx1
xaResource.end(xid, XAResource.TMSUCCESS);
int result = xaResource.prepare(xid);
if (result != XAResource.XA_OK) {
throw new RuntimeException("tx1 prepare returns unsupported code: "+result);
}
xaResource.rollback(xid);
xaConn.close();
...
Note that tx1 is rolled back at the end while tx2 is committed. Even though we're sharing JDBC Connection (end even Statement) across two JTA transactions here if JDBC driver correctly supports XA/JTA the result after the program run should be only one new record in the 'a' table - the one from committed transaction tx2 (I run this with Oracle 9i and JDBC thin 10g driver).
After transaction is suspended you don't have to start new XA transaction but can continue to reuse the same JDBC Connection within local transaction (calling commit()/rollback() on Connection instead of on XAResource).
The code similar to above is commonly executed by TransactionManager implementation in J2EE servers. As a matter of fact, TransactionManager is allowed to call suspend/resume as many times as he think necessary even during lifecycle of a transaction, possibly for better resources handling etc.
Described functionality theoretically should be applicable to any XAResource, not only JDBC Connection.
Technorati Tags:
Java
JTA
XA
Transactions
Oracle
( Aug 18 2005, 02:00:22 PM EDT )
Permalink
Hacking XA Transaction Manager
Sometimes there is a need to use JDBC XAConnection without support from TransactionManager (outside of J2EE container for example). It's good to know that it's not that complicated to emulate work performed by TM (obviously not all of the TM functionality but just a 'normal flow' scenario. If you want to get into Heuristics resolutions/recovery etc. it gets much more complicated rather quickly).
Example below works with Oracle JDBC driver 10g (I suspect that any other JDBC driver with support for XA should work too, only one Oracle-specific class is OracleXADataSource which should be replaced with provider specific implementation).
// This class demonstrates manual operations with XAResource
// without TransactionManager
public class XaTest {
public static void main(String[] args) throws Exception {
Class.forName("oracle.jdbc.xa.client.OracleXADataSource");
// Create XA DataSource
OracleXADataSource xaDs = new OracleXADataSource();
xaDs.setURL("jdbc:oracle:thin:@host:1521:SID");
xaDs.setUser("xxx");
xaDs.setPassword("xxx");
XAConnection xaConn = xaDs.getXAConnection();
XAResource xaResource = xaConn.getXAResource();
Xid xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x01});
xaResource.start(xid, XAResource.TMNOFLAGS);
// (1)
// regular JDBC operations
Connection conn = xaConn.getConnection();
Statement st = conn.createStatement();
st.executeUpdate("insert into a(a_name) values('test 1'");
st.close();
conn.close();
// (2)
xaResource.end(xid, XAResource.TMSUCCESS);
int result = xaResource.prepare(xid);
if (result != XAResource.XA_OK) {
throw new RuntimeException("prepare returns unsupported code: "+result);
}
// commit or rollback
xaResource.commit(xid, false);
//xaResource.rollback(xid);
xaConn.close();
}
}
MyXid if trivial implementation of Xid interface which is sufficient for above example:
public class MyXid implements Xid {
private final int formatId;
private final byte[] globalTransactionId;
private final byte[] branchQualifier;
public MyXid(int formatId, byte globalTransactionId[], byte branchQualifier[]) {
this.formatId = formatId;
this.globalTransactionId = globalTransactionId;
this.branchQualifier = branchQualifier;
}
public int getFormatId() {
return this.formatId;
}
public byte[] getBranchQualifier() {
return this.branchQualifier;
}
public byte[] getGlobalTransactionId() {
return this.globalTransactionId;
}
}
In a real implementation TransactionManager should generate unique values for globalTransactionId for every XA transaction and branchQualifier for every transactional branch.
You can see all important steps in XA transaction lifecycle from the code above: start() , end() , prepare() and commit() calls. This provides oversimplification but close-enough picture of what's going on inside JTA TransactionManager. Basically code similar to code before (1) marker is executed by J2EE container when application requests Connection from XA DataSource and code after (2) marker is executed at some point after conne.close() .
If you comment out xaResource.commit(xid) from the code above, subsequent execution of the test would fail on start() because we're re-using the same Xid and Oracle persisted information that this global transaction was prepared but never committed. You can see that by executing select from sys.PENDING_TRANS$ or dba_pending_transactions for example. In real life such condition could happen when there is a failure (communication, resource, TM etc) at some point in XA transaction and real implementation of TransactionManager should execute recover() call on every XAResource to get Xids which needs to be recovered and decide what to do with it:
...
Xid[] xids = xaResource.recover(0);
for (int i = 0; i < xids.length; i++) {
Xid xid = xids[i];
// check that it's our xid
...
// forget this transaction, for example
xaResource.forget(xid);
}
...
Read excellent 'XA Exposed' series by Mike Spille ( parts 1, 2 and 3) if you want better understanding how JTA TransactionManager works.
Technorati Tags:
Java
JTA
XA
Transactions
Oracle
( Aug 18 2005, 01:59:29 PM EDT )
Permalink
Comments [2]
Friday July 29, 2005
Wexford wreck, Lake Huron
We dove a Wexford wreck on Lake Huron two weeks ago. Charter was run by Sean Stoeten from SoScuba which I recommend highly.
We drove from Toronto to
Grand Bend, ON where Sean was waiting us with his boat. Weather was calm and it's a quick ride on his fast boat to the wreck which is just couple miles off shore. Wreck is moored at three points - bow, stern and at midship. First thing you noticed in the Lake Huron is blue water, so much nicer than greenish water in Ontario where we usually diving, it feels just like an ocean. Visibility was outstanding from the surface, you can spot the wreck 50 feet below right as you start descend down the line but under the termocline, just above the wreck vis drops dramatically, which is kind of unusual. Wreck itself is massive, 250 feet long, standing on the bottom perfectly horizontal. You can easily spend multiple dives on it, there is so much stuff to see - potholes, anchors, some china and instruments below deck. Excellent penetration as well but beware of bad visibility and lot of sediment, do it only if you perfected your boyuncy.
Overall excellent dive site, if you in the area it worth to explore. Check out more pictures on Vlada's website.
Technorati Tags:
Scuba
Diving
Great Lakes
( Jul 29 2005, 11:03:48 PM EDT )
Permalink
Hibernate: Batch update returned unexpected row count
It's amazing how people finding this blog by searching Google for something different. Sometimes it gives me ideas for a new posts (kind of 'you have asked, we're answering' approach). So by popular demand ;-) today's subject is Unexpected row count error happening in Hibernate. That was a good excuse to try Hibernate 3 and new support for EJB3 annotations in it.
When do you see this kind of error in Hibernate? When your code is trying to update objects and some of them getting changed at the same time in different transaction. When executing update Hibernate keep track of number of rows affected in database by DML operation and throw StaleStateException if number of updated rows are less than expected (some records which your original query returned cannot be found with this criteria anymore) or just generic HibernateException in opposite case. Below is simple jUnit test to illustrate this behaviour:
public class BatchTest extends TestCase {
// Hibernate objects
private SessionFactory sf;
private Session session;
// standlone JDBC connection
private Connection connection;
@Override
protected void setUp() throws Exception {
super.setUp();
this.sf = new AnnotationConfiguration()
.configure("hibernate.cfg.xml").buildSessionFactory();
this.session= this.sessionFactory.openSession();
Class.forName(DRIVER);
this.connection = DriverManager.getConnection(URL, USER, PASSWORD);
}
@Override
protected void tearDown() throws Exception {
try {
this.session.close();
this.sf.close();
this.connection.close();
}
finally {
super.tearDown();
}
}
@SuppressWarnings({"boxing","unchecked"})
public void testBatch() throws Exception {
Transaction transaction = this.session.beginTransaction();
List persons = this.session
.createQuery("from Person as p order by p.id").list();
final int size = persons.size();
int count = 1;
for(Person p : persons) {
System.err.println("id="+p.getId());
if (count == size) {
// update last row outside of this transaction
updateRow(p.getId());
}
p.setName("foo");
count++;
}
// commit transaction
try {
transaction.commit();
fail("Expected exception is not thrown");
}
catch (HibernateException e) {
// good
assertTrue("Unexpected exception message: "
+e, e.getMessage().indexOf("Batch update returned unexpected row count from update") >= 0);
}
}
// update row in different transaction
@SuppressWarnings("boxing")
private void updateRow(Long personId) throws SQLException {
System.err.println("updating row id="+personId);
PreparedStatement ps = null;
try {
ps = this.connection.prepareStatement("update person set pers_id=pers_id+1 where pers_id=?");
ps.setLong(1, personId);
int rowsUpdated = ps.executeUpdate();
assertEquals("Updated unexpected number of rows", 1, rowsUpdated);
}
finally {
if (ps != null) {
ps.close();
}
}
}
}
Test method starts a new JDBC transaction, executes query on Person object and while iterating over results updates Person's name attribute. For the last element in List it also calls updateRow method which takes Person's primary key and update the row in database using separate JDBC Connection , therefore emulating concurrent modification of data by different process/client. As evident from the test Hibernate defers actual execution of DML until transaction commit time and while executing update it checks for number of rows affected (pretty much in the same way as assertion written in updateRow ). The result is exactly as expected:
2005-07-28 11:43:19,188 ERROR (org.hibernate.jdbc.AbstractBatcher:doExecuteBatch:60) - Exception executing batch:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 11 actual row count: 0 expected: 1
at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:92)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:78)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:57)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:174)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:137)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:324)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
at com.maximdim.hb3.batch.BatchTest.testBatch2(BatchTest.java:71)
...
Good to note that we only got this exception because we altered data in the way that updated row is not part of original result set returned by query anymore. If for example we have updated Person name instead of id in updateRow() this exception won't happen and you'd get 'lost update' problem. Consider using Optimistic concurrency control if you need to detect/recover from it.
Technorati Tags:
Java
Hibernate
( Jul 29 2005, 03:18:59 PM EDT )
Permalink
Comments [5]
Friday July 22, 2005
CruiseControl Monitor extension now supports Mozilla Thunderbird
CruiseControl Monitor extension starting from version 0.8 supports Mozilla Thunderbird.
Available for download from here
( Jul 22 2005, 10:56:15 AM EDT )
Permalink
Comments [0]
Thursday July 21, 2005
Bean-Managed Transaction Suspension in J2EE
My article about Bean-Managed Transaction Suspension in J2EE has been published by O'Reilly's onJava.
I tried to cover different techniques of obtaining reference to TransactionManager in popular J2EE servers and using it for suspend/resume transactions as well as known problems with it. This allows developer to achieve the same transactional attributes which available when using Container-Managed transactions in Bean-Managed code. Couple of words about usage of TransactionManager in Spring framework as well.
See also related article by Juergen Hoeller which covers this topic from Spring prospective in more details.
( Jul 21 2005, 09:43:54 AM EDT )
Permalink
Comments [0]
Monday July 18, 2005
CruiseControl.NET Monitor extension for Firefox
CruiseControl Monitor extension starting from version 0.7.1 supports CruiseControl.NET projects.
Available for download from here
( Jul 18 2005, 11:11:30 PM EDT )
Permalink
Thursday July 14, 2005
CruiseControl Monitor plugin for Firefox 0.7 released
Available for download from here
New in this version:
- Support for sorting of projects in tooltip by name or last build time
- Support for including only failed projects in tooltip
- Proportional font is back, now with proper alignment
- Visual indication of failed projects (*) in addition to color, for colorblind users or users with monochrome monitors
( Jul 14 2005, 10:35:55 AM EDT )
Permalink
Friday July 08, 2005
CruiseControl Monitor plugin for Firefox 0.6.1 released
Available for download from here
New in this version:
- Proportional font in tooltip with alignment
- Support for include/exclude project lists (with regexp)
- Update mechanism rewritten to update status faster after Options change
- Extension icon
Planning future features:
- Multiple CruiseControl servers support (not clear how to best show status - aggregated across all servers or one per server?)
- Make projects list sortable by name/last build time
- Other ideas or suggestions? Leave comment here or drop me a line
( Jul 08 2005, 11:18:04 AM EDT )
Permalink
Comments [0]
|