Wednesday, May 28, 2008

YUI study notes - Event

The window.onload event is synchronous with images and external dependencies, so it doesn't fire until they've all resolved (or timed out), also in some browser (IE), the target elements that do not yet exist when onload event fired. What we really want is an event that fires when the DOM ready, YUI's onDOMReady(), onAvailable() and onContentReady () help to avoid this problem.

Event object is accessed differently in different browser. In IE the event object is accessed through the window object, whereas in Firefox, it is automatically passed to your event handler as an argument.

[1] has a good explanation on the callback function signature.

Callback for Native Browser Events


var callback = function(e, o) {
alert(o.msg);
};

YAHOO.util.Event.addListener(elemId, "click", callback, {msg: "hi"});

Callback for Custom Events
If the custom event is fired with parameter signature YAHOO.util.CustomEvent.LIST, which is by default

var callback = function(e, a, o) {
//a is an array contains arguments fire method: arg1, arg2, ...
//o is the object passed from the subscribe method
alert(o.msg);
};

//to fire this event
eventObj.fire(arg1, arg2, ...);

//to listen to this event
eventObj.subscribe(callback, {msg: "hi"});

where
e - Name of this custom event.
a - Arguments that passed to the fire method of this event.
o - Object that passed as a parameter to this callback function.

If the custom event is fired with parameter signature YAHOO.util.CustomEvent.FLAT

var callback = function(a, o) {
//a is the first argument that passed to fire method of this event
//o is the object passed from the subscribe method
alert(o.msg);
};

//to fire this event
eventObj.fire(arg);

//to listen to this event
eventObj.subscribe(callback, {msg: "hi"});

Monday, May 26, 2008

Redirect-after-Post pattern and Session bound ActionMessages

To deal with double submit problem, the Redirect after Post pattern is purposed in [1] and [2].
- Never show pages in response to POST
- Always load pages using GET
- Navigate from POST to GET using REDIRECT

To facilitate this, the errors/messages need to be stored in session scope, o/w they would get lost after redirect. From Struts 1.2.4, Action.saveMessages(HttpSession, ActionMessages) is added [3]. Upon the first access to the messages they are effectively removed from the session. This allows messages to be retained when using the redirect after post pattern.

ActionRedirect inherits from ActionForward, with support for adding parameters at runtime.

Wednesday, May 21, 2008

MySql straight join and index hint


SELECT STRAIGHT_JOIN p.NAME AS NAME,
    ROUND(AVG(r.VAL)) AS AVGVAL,
    MIN(r.MINVALUE) AS MINVAL,MAX(r.MAXVALUE) AS MAXVAL
FROM reports_hourly AS r force index (PRIMARY), polleddata AS p,wirelessaccesspoint ap
WHERE p.OID = ? AND p.ID=r.POLLID
  AND r.ttime >= ? AND p.name=ap.name AND ap.aprole=0
GROUP BY p.NAME
ORDER BY AVGVAL DESC
LIMIT 0,5

* Table reports_hourly is the biggest table, contains millions records. Its primary key index is on (ttime, pollid). If move it to the last table to join, the query would run much slower.

Tuesday, May 20, 2008

MySql innodb transaction/logs



- MySql's innodb follows Oracle's multi-version read consistency model. It's multi version is based on undo log. In Innodb, each table has two hidden columns, a transaction number and a pointer to the previous version of that row in the undo log. With the rollback pointer, READ COMMITTED can go back one step to the past, whereas REPEATABLE READ can go back multiple steps until it reaches the newest version after its transaction starts. Based on the transaction number, we know particular row in data buffer is associated with committed or uncommitted transaction.

- Write-Ahead logging (WAL) principal: all modifications are written to a log before they are applied to the database. It is to guarantee the Atomicity and Durability of ACID. WAL significantly reduces the number of disk writes, since only the log file needs to be flushed to disk at the time of transaction commit. In multi-user environments, commits of many transactions may be accomplished with a single fsync() of the log file. The log file is written sequentially, so the cost of syncing the log is much less than the cost of flushing the data pages. Writing back change to table space involves slow disk seek rather than liner write. Another benefit of WAL is consistency of the data pages.

- Two buffers: log buffer and data buffer in InnoDb. Log buffer is for insert/update, whereas data buffer is for all operations, it used to cache data and indexes of the tables. Also there are some other memory buffer such as sort buffer and query cache buffer. When a transaction commit, the rows in log buffer are flushed to disk, whereas the rows in data buffer are marked as dirty but not flushed. When innodb_flush_log_at_trx_time set to 0, even the log buffer is not flushed (not recommended). There are several threads monitoring buffer activity, and three situations -- overflow, checkpoint, and commit -- that result in disk writes.

- InnoDB uses both row level locking and multi-versioning concurrency control (MVCC). Multi-version Consistent read is the default mode in which InnoDB processes SELECT statements in READ COMMITTED and REPEATABLE READ isolation levels. FOR UPDATE or IN SHARE MODE apply exclusive (X) lock and shared (S) lock.

- Checkpoint: InnoDB takes some pro-active measures against overflows, and the most important of these measures is checkpointing. There is a separate thread, or a combination of threads that are separate from the thread that changes the buffers. At fixed intervals the checkpointer will wake, look for buffer changes, and ensure that writes happen.

See:
Transactions - An InnoDB tutorial
How Logs work on MySQL with InnoDB tables

Thursday, May 15, 2008

HttpSessionBindingListener vs. HttpSessionAttributeListener

HttpSessionBindingListener:
If an object implements HttpSessionBindingListener, it is notified when it is bound to or unbound from a session. For example,


MyObject implements HttpSessionBindingListener
{
// class definition
}

If I call

session.setAttribute ("Object", MyObject)

or

session.removeAttribute ("Object", MyObject)

and so on, methods valueBound and/or valueUnbound (defined in HttpSessionBindingListener, implemented in MyObject are called)

When any class implements HttpSessionAttributeListener interface it is notified when any change in the attribute list of session occurs. For example

MyClass implements HttpSessionAttributeListener
{
// implementations of methods
}


session.setAttribute ("anything", AnyObjectNotOnlyMyClass);

or

session.removeAttribute ("anything", AnyObjectNotOnlyMyClass);


indicates change in the list of attributes and hence appropriate method is called

Differences:
Implementing HttpSessionBindingListener works only for the object that implements it whereas implementing HttpSessionAttributeListener listens to any attribute added, removed or replaced.

See:Difference between HttpSessionBindingListener and HttpSessionAttributeListener.

Friday, May 9, 2008

JMX service URL




A Remote JMX Client is just like a Local JMX Client except that it must instantiate a Connector client and connect it up to a Connector server to get a MBeanServerConnection. It then uses the MBeanServerConnection in the same way as a Local JMX Client. Remote JMX applications can not work with an MBeanServer object, only an MBeanServerConnection.

The Agent must run a Connector Server using the same tranport protocol that you will use on the client side. If your server serves tranport "rmi", then your client(s) need to use protocol "rmi". Other protocols are jmxmp, http, snmp. RMI is the default transport protocol. So we have JMX Connector client and JMX connector server for different protocols.

On connector server side:


// Instantiate the MBean server
MBeanServer mbs = MBeanServerFactory.createMBeanServer();
// Create an RMI connector server
JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi://srv:20000/jndi/rmi://srv:30000/server");
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url,null, mbs);
cs.start();

The connector server cs is launched by calling the start() method of JMXConnectorServer, whereupon the instance of RMIConnectorServer that is created exports its underlying RMI server stub server to the RMI registry.

On connector client side:

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://srv:20000/jndi/rmi://srv:30000/server");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

As you can see, the Client defines the same service URL url as that defined by Server. This allows the connector client to retrieve the RMI connector server stub named server from the RMI registry running on port 30000 of the srv host, and to connect to the RMI connector server.

In javax.management.remote.rmi doc, the service URL is explained in detail. A simplified URL is service:jmx:rmi:///jndi/rmi://srv:30000/server. The absent host means local host, and the absent port means the the exported remote object RMIServerImpl_Stub is not exported to a specified port. Remember when we export a remote object, The second argument in exportObject method, specifies which TCP port to use to listen for incoming remote invocation requests for the object. It is common to use the value zero, which specifies the use of an anonymous port. The actual port will then be chosen at runtime by RMI or the underlying operating system.

Compute engine = new ComputeEngine();
Compute stub =(Compute) UnicastRemoteObject.exportObject(engine, 0);Registry registry = LocateRegistry.getRegistry();
registry.rebind("computer", stub);

At last, the "jndi" in the middle of the url means we use JNDI to look up the remote object. RMIRegistry is just one of its service provider, others include file system, ldap, etc. So we may have jndi/ldap:localhost:839, jndi/c:/myphotos, etc.

[Reference]
Monitor Your Applications With JConsole
Remote JMX, RMI and JMXServiceURL
Implementing a remote interface
RMI Registry Service Provider for the Java Naming and Directory InterfaceTM (JNDI)

Wednesday, May 7, 2008

RMI stub, tomcat classloader, ..

RMI client look up RMI registry for remote object's stub instance.


LicServer licServer = (LicServer) Naming.lookup("//localhost:1099/LicServerAPI");

It return an instance of LicServerImpl_Stub. In run time, client need the Stub class to resolve it. It obtains it from local classpath or codeBase (remote classpath). The codeBase approach is to download the class bytecode via http, by setting -Djava.rmi.server.codebase=http://hostname/locationOfClasses

In our system, the RMI client resides on the same machine as a web app in an embedded Tomcat launched by webnms. Hence, it is able to get the stub files by classpath.

The Tomcat classloading has a hierarchy structure.
- Bootstrap: JVM classes
- System: based on system CLASSPATH, however, the default startup script catalia.bat ingore it and only pass in BootStrap.jar to start up.
- Common: shared by all web app and tomcat server's internal classes
- webapps: each web app has it own classloader. This web application classloader doesn't follow the common parent delegation model except those java.* classes according to servlet spec recommendation.

To add classpath to system classloader, we can modify the classpath in script, or when launching embeded tomcat, we can pass server's classpath to it by

Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
Class class1 = Class.forName("org.apache.catalina.startup.Bootstrap");
Method method = class1.getMethod("main", aclass);
method.invoke(null, aobj);

Applications written in statically compiled programming languages, such as C and C++, are compiled into native, machine-specific instructions and saved as an executable file. The process of combining the code into an executable native code is called linking - the merging of separately compiled code with shared library code to create an executable application. This is different in dynamically compiled programming languages such as Java. In Java, the .class files generated by the Java compiler remain as-is until loaded into the Java Virtual Machine (JVM) -- in other words, the linking process is performed by the JVM at runtime. Classes are loaded into the JVM on an 'as needed' basis. And when a loaded class depends on another class, then that class is loaded as well.

It gives us the flexibility to control the class loading, e.g., apply security policy, loading from remote place, etc.