Saturday, May 26, 2012


Designing Swing Framework - Layout Managers


Why so much fuss about Java swings layout managers?


Windows programmers always tend to think about why Java makes so much fuss about layout managers. In Windows, its not a big task. You just have to use a dialogue editor to drag and drop components and place them appropriately to get a very professional looking GUI.

The problem with this approach is that the resulting layout must be manually updated if the size of the components changes. Why would the component size change? There are two common cases. First, a user may choose a larger font for button labels and other dialog text. If you try this out for yourself in Windows, you will find that many applications deal with this exceedingly poorly. The buttons do not grow, and the larger font is simply crammed into the same space as before. The same problem can occur when the strings in an application are translated to a foreign language. For example, the German word for “Cancel” is “Abbrechen.” If a button has been designed with just enough room for the string “Cancel”, then the German version will look broken, with a clipped command string.

Why don’t Windows buttons simply grow to accommodate the labels? Because the designer of the user interface gave no instructions in which direction they should grow. After the dragging and dropping and arranging, the dialog editor merely remembers the pixel position and size of each component. It does not remember why the components were arranged in this fashion.

Layout Managers in Java Swings

The Java Swings layout managers are a much better approach to component layout. With a layout manager, the layout comes with instructions about the relationships among the components. This was particularly important in the original AWT, which used native user interface elements. The size of a button or list box in Motif, Windows, and the Macintosh could vary widely, and an application or applet would not know a priori on which platform it would display its user interface. To some extent, that degree of variability has gone away with Swing. 

GridBagLayout is the lay out provided in JDK 1.0 and is the most successful layout manager developed so far.GridBagLayout is one of the most flexible and complex  layout managers the Java platform provides. A GridBagLayout places components in a grid of rows and columns, allowing specified components to span multiple rows or columns. Not all rows necessarily have the same height. Similarly, not all columns necessarily have the same width. Essentially, GridBagLayout places components in rectangles (cells) in a grid, and then uses the components' preferred sizes to determine how big the cells should be.

GridBag Layout appears quite complex to a new programmer.In order to cater to this problem Swing designers came up with the box layout. However, because each box is laid out independently, you cannot use box layouts to arrange neighboring components both horizontally and vertically. Hence it failed.

Java SE 1.4 saw yet another attempt to design a replacement for the grid bag layout—the spring layout. You use imaginary springs to connect the components in a container. As the container is resized, the springs stretch or shrink, thereby adjusting the positions of the components. This sounds tedious and confusing, and it is. The spring layout quickly sank into obscurity.

In 2005, the NetBeans team invented the Matisse technology, which combines a layout tool and a layout manager. A user interface designer uses the tool to drop components into a container and to indicate which components should line up. The tool translates the designer’s intentions into instructions for the group layout manager. This is much more convenient than writing layout management code by hand. The group layout manager is now a part of Java SE 6. This background code for this layout manager quickly becomes quite lenghty and complex when few components are added vertically and horizontally with some deletion of compenets during development.


All about GridBagLayout Manager


The way java program specifies the size and position characteristics of its components is by specifying constraints for each component. The preferred approach to set constraints on a component is to use the Container.add variant, passing it a GridBagConstraints object.
Following section is straight from sun Java documentation

gridx, gridy:

Specify the row and column at the upper left of the component. The leftmost column has address gridx=0 and the top row has address gridy=0. Use GridBagConstraints.RELATIVE (the default value) to specify that the component be placed just to the right of (for gridx) or just below (for gridy) the component that was added to the container just before this component was added. We recommend specifying the gridx and gridy values for each component rather than just using GridBagConstraints.RELATIVE; this tends to result in more predictable layouts.

gridwidth, gridheight:

Specify the number of columns (for gridwidth) or rows (for gridheight) in the component's display area. These constraints specify the number of cells the component uses, not the number of pixels it uses. The default value is 1. Use GridBagConstraints.REMAINDER to specify that the component be the last one in its row (for gridwidth) or column (for gridheight). Use GridBagConstraints.RELATIVE to specify that the component be the next to last one in its row (for gridwidth) or column (for gridheight). We recommend specifying the gridwidth and gridheight values for each component rather than just using GridBagConstraints.RELATIVE and GridBagConstraints.REMAINDER; this tends to result in more predictable layouts.

fill:

Used when the component's display area is larger than the component's requested size to determine whether and how to resize the component. Valid values (defined as GridBagConstraints constants) include NONE (the default), HORIZONTAL (make the component wide enough to fill its display area horizontally, but do not change its height), VERTICAL (make the component tall enough to fill its display area vertically, but do not change its width), and BOTH (make the component fill its display area entirely

ipadx, ipady:

Specifies the internal padding: how much to add to the size of the component. The default value is zero. The width of the component will be at least its minimum width plus ipadx*2 pixels, since the padding applies to both sides of the component. Similarly, the height of the component will be at least its minimum height plus ipady*2 pixels.

insets:

Specifies the external padding of the component -- the minimum amount of space between the component and the edges of its display area. The value is specified as an Insets object. By default, each component has no external padding.

anchor:

Used when the component is smaller than its display area to determine where (within the area) to place the component. Valid values (defined as GridBagConstraints constants) are CENTER (the default), PAGE_START, PAGE_END, LINE_START, LINE_END, FIRST_LINE_START, FIRST_LINE_END, LAST_LINE_END, and LAST_LINE_START.


Making it Simple


NetBeans provides a graphical representation to remeber the meaning of above constraints . see the figure below.

Avoiding GridBagLayout Issues


The AWT documentation recommends that instead of setting the gridx and gridy values to absolute positions, you set them to the constant GridBagConstraints.RELATIVE. Then, add the components to the grid bag layout in a standardized order, going from left to right in the first row, then moving along the next row, and so on.
You still specify the number of rows and columns spanned, by giving the appropriate gridheight and gridwidth fields. Except, if the component extends to the last row or column, you aren’t supposed to specify the actual number, but the constant GridBagConstraints.REMAINDER. This tells the layout manager that the component is the last one in its row.


The way framework programmer works


The most tedious aspect of the grid bag layout is writing the code that sets the constraints. Most programmers write helper functions or a small helper class for this purpose. Following is an example of one such helper class. Following are its features. Full class has been provided at the end of this section.

-Its name is short: GBC instead of GridBagConstraints.

-It extends GridBagConstraints, so you can use shorter names such as GBC.EAST for the constants.

-Use a GBC object when adding a component, such as
add(component, new GBC(1, 2));

-There are two constructors to set the most common parameters: gridx and gridy, or gridx, gridy, gridwidth, and gridheight.
add(component, new GBC(1, 2, 1, 4));

-There are convenient setters for the fields that come in x/y pairs:
add(component, new GBC(1, 2).setWeight(100, 100));

-The setter methods return this, so you can chain them:
add(component, new GBC(1, 2).setAnchor(GBC.EAST).setWeight(100, 100));

-The setInsets methods construct the Insets object for you. To get one-pixel insets, simply call
add(component, new GBC(1, 2).setAnchor(GBC.EAST).setInsets(1));

GBC.Java

package com.example.layout;

import java.awt.GridBagConstraints;
import java.awt.Insets;

public class GBC extends GridBagConstraints {

public GBC (int gridx, int gridy) {
this.gridx=gridx;
this.gridy=gridy;
}
public GBC (int gridx, int gridy, int gridwidth, int gridheight) {
this.gridx=gridx;
this.gridy=gridy;
this.gridwidth=gridwidth;
this.gridheight=gridheight;
}
public GBC setFill(int fill) {
this.fill=fill;
return this;
}
public GBC setAnchor(int anchor) {
this.anchor=anchor;
return this;
}
public GBC setInsets(int distance) {
this.insets=new Insets(distance, distance, distance, distance);
return this;
}
public GBC setInsets(int top, int left, int bottom, int right ) {
this.insets=new Insets(top, left, bottom, right);
return this;
}
public GBC setWeight(double weightx, double weighty) {
this.weightx=weightx;
this.weighty=weighty;
return this;
}
public GBC setIpad(int ipadx, int ipady) {
this.ipadx=ipadx;
this.ipady=ipady;
return this;
}
}

Test Example Class

package com.example.layout;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;


public class OWLODF {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
OdfFrame frame = new OdfFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

}
/**
 * Default values
 * Remainder= 0, Relative= -1
 * gridx = Default:GridBagConstraints.RELATIVE=-1, it should be a non-negative value.gridx defines which column
 * gridy = Default:GridBagConstraints.RELATIVE=-1, it should be a non-negative value.gridy defines which row
 * gridwidth = Default: value=1.Use REMAINDER to specify that the component's display area will be from gridx to the last cell in the row. 
 *   Use RELATIVE to specify that the component's display area will be from gridx to the next to the last one in its row.
 * Default value=1, it should be non-negative. gridwidth defines span in terms of column.In other words how many columns it should occupy
 * gridheight = Default: value=1.Use REMAINDER to specify that the component's display area will be from gridy to the last cell in the column. 
 * Use RELATIVE to specify that the component's display area will be from gridy to the next to the last one in its column
 * gridheight should be a non-negative value and the default value is 1.gridheight defines span in terms of row.In other words how many rows it should occupy
 * weightx = Default 0, non-negative. If 1 it will expand horizontally .Zero means it wont expand in x dir. Better to keep it 0 and disbale resizing of frame
 * weighty = Default 0, non-negative. If 1 it will expand vertically .Zero means it wont expand in y dir. Better to keep it 0 and disbale resizing of frame
 * fill = Default:NONE
 * anchor = Default: CENTRE. There are three possible orientations, namely relative(e.g.PAGE_START, LINE_START etc), baseline relative (e.g BASELINE_LEADING, BASELINE_TRAILING etc) and absolute(e.g. NORTH, SOUTH etc).
 * insets = Default: Insets(0, 0, 0, 0). this is for external padding
 * ipadx = Default: 0
 * ipady = Default: 0
 * @author Lenovo
 *
 */
class OdfFrame extends JFrame{
int G_XY_DEFAULT = -1;
int G_WH_DEFAULT = 1;
int REMAINDER= GridBagConstraints.REMAINDER;//0
int RELATIVE=GridBagConstraints.RELATIVE;//-1
double WT_X_DEFAULT=0;
double WT_Y_DEFAULT=0;
int IPDX_DEFAULT=0;
int IPDY_DEFAULT=0;
OdfFrame(){
this.setTitle("ODF BB Client");
JPanel mainPanel = new JPanel();
mainPanel.setBorder(new TitledBorder("Main Panel"));
mainPanel.setName("Main Panel");
GridBagLayout mainGBL = new GridBagLayout();
mainPanel.setLayout(mainGBL);
JPanel panel_1 = new JPanel();
panel_1.setBorder(new TitledBorder("Process_1"));
GridBagLayout procGBL_1 = new GridBagLayout();
panel_1.setLayout(procGBL_1);
JLabel owlAgent_1 = new JLabel("OWL Agent_1");
JTextField owlAgentF_1 = new JTextField(15);
owlAgentF_1.setText("owlAgentF_1");
JLabel system_1 = new JLabel("System_1");
JTextField systemF_1 = new JTextField(15);
systemF_1.setText("systemF_1");
JLabel process_1 = new JLabel("Process_1");
JTextField processF_1 = new JTextField(15);
processF_1.setText("processF_1");
JLabel instance_1 = new JLabel("Instance_1");
JTextField instanceF_1 = new JTextField(15);
instanceF_1.setText("instanceF_1");
/////
JPanel panel_2 = new JPanel();
panel_2.setBorder(new TitledBorder("Process_2"));
GridBagLayout procGBL_2 = new GridBagLayout();
panel_2.setLayout(procGBL_2);
JLabel owlAgent_2 = new JLabel("OWL Agent_2");
JTextField owlAgentF_2 = new JTextField(15);
owlAgentF_2.setText("owlAgentF_2");
JLabel system_2 = new JLabel("System_2");
JTextField systemF_2 = new JTextField(15);
systemF_2.setText("systemF_2");
JLabel process_2 = new JLabel("Process_2");
JTextField processF_2 = new JTextField(15);
processF_2.setText("processF_2");
JLabel instance_2 = new JLabel("Instance_2");
JTextField instanceF_2 = new JTextField(15);
instanceF_2.setText("instanceF_2");
////
///////
JPanel panel_3 = new JPanel();
panel_3.setBorder(new TitledBorder("Process_3"));
GridBagLayout procGBL_3 = new GridBagLayout();
panel_2.setLayout(procGBL_3);
JTextArea area = new JTextArea(8,40);
JScrollPane scrollPane = new JScrollPane(area);

area.setText("this is a text area");
area.setWrapStyleWord(true);
area.setLineWrap(true);
addComponentsToPanel(panel_1, owlAgent_1,  defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 20, 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_1, owlAgentF_1, defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_1, system_1,    defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_1, systemF_1,   defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_1, process_1,   defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_1, processF_1,  defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, REMAINDER,    G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_1, instance_1,  defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, REMAINDER   , 5, 20, 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_1, instanceF_1, defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
////
addComponentsToPanel(panel_2, owlAgent_2,  defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 20, 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_2, owlAgentF_2, defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_2, system_2,    defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_2, systemF_2,   defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_2, process_2,   defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_2, processF_2,  defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, REMAINDER,    G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_2, instance_2,  defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, REMAINDER   , 5, 20, 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(panel_2, instanceF_2, defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, G_WH_DEFAULT, G_WH_DEFAULT, 5, 5 , 5, 30, WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
////
addComponentsToPanel(panel_3, scrollPane,  defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, REMAINDER, G_WH_DEFAULT, 5, 20, 5, 5 , WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(mainPanel, panel_1,   defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, REMAINDER   , G_WH_DEFAULT, 5, 5, 5, 5 ,  WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(mainPanel, panel_2,   defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, REMAINDER   , G_WH_DEFAULT, 5, 5, 5, 5 ,  WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
addComponentsToPanel(mainPanel, panel_3,   defineConstraints(G_XY_DEFAULT, G_XY_DEFAULT, REMAINDER   , G_WH_DEFAULT, 5, 5, 5, 5 ,  WT_X_DEFAULT, WT_Y_DEFAULT, IPDX_DEFAULT, IPDY_DEFAULT, GridBagConstraints.NONE, GridBagConstraints.WEST));
this.add(mainPanel);

}
public void addComponentsToPanel(JPanel panel, JComponent component,GBC constraints) {
panel.setBackground(Color.WHITE);
panel.add(component, constraints);
}
public GBC defineConstraints(int gridx,int gridy,int gridwidth, int gridheight, int topSpace, int leftSpace, int bottomSpace, int rightSpace,double weightx, double weighty,int ipadx, int ipady, int fill, int anchor) {
return new GBC(gridx, gridy, gridwidth, gridheight).setInsets(topSpace, leftSpace, bottomSpace, rightSpace ).setWeight(weightx, weighty).setIpad(ipadx, ipady).setFill(fill).setAnchor(anchor);
}
}

Wednesday, December 28, 2011

Linux process memory usage calculations

It is difficult to obtain the actual memory consumption of a process in Linux/Unix environment.We cannot always pin-point the exact memory usage of a process as most processes on Linux uses shared library.

Assume that we want to calculate memory usage for the 'ls' process. Do we count only the memory used by the executable 'ls' ? How about libc? Or all these other libs that are required to run 'ls' ? One could argue that they are shared by other processes, but 'ls' can't be run on the system without them being loaded.Also, if we need to know how much memory a process needs in order to do capacity planning, we would need to calculate how much each additional copy of the process uses

A close approximation of memory consumtion of a process is being provided by VSS(virtual Set Size) of the process.

We can obtain the VSS value from 'ps aux' command line. This is handy when we just need to view the process memory consumption once in a while.

However, the use of 'ps' command is not suitable when designing an application which reports system health. Imagine a health-check application which needs to report the memory consumtion of some resource intensive process running on the box. If the health-check app itself consumes 4-5% of memory as it needs to run say every 10sec then it adds an unnecessary overhead on the Linux/Unix host which is already resource starved. 'ps' command itself is taxing on resource memory utilisation.The same goes true for other commands e.g top, vmstat, etc

So to increase the efficiency of the program reporting the system health, we should avoid the use of 'ps' command. So what alternatives are available to obtain the VSS of a process?

VSS information can be obtained from /proc filesystem.VSS information is present in 23rd argument in /proc/$pid/stat. This is the same place from where ps command obtains its value. Its value can be obtained from /proc/$pid/status from VmSize value as well./proc/$pid/smaps also provide this information in detailed breakdown manner but is not worth the effort in most of the scenarios as the value of VSS is a close approximation to the actual memory being used by the system.

For the sake of calculating the %age memory consumtion by a process we can obtain the system memory consumtion from file /proc/meminfo. The argumenet MemTotal depicts the total memory consumption by the system.

The most efficient and easy way is to obtain it from a C/C++ program reading the /proc filesystem.C language is preferable on the consistency front as all Unix/Linux kernel supports C.

Thursday, December 1, 2011

Singletons in Java - An Analysis

The "classic" practical example...
import java.io.PrintStream;

public class LogManager {
 private static LogManager instance;
 private PrintStream pStream;

 private LogManager(PrintStream out) {
  pStream = out;
 }

 public static LogManager getInstance() {
  if (instance == null)
   instance = new LogManager(System.out);
  return instance;
 }

 public void log(String msg) {
  pStream.println(msg);
 }
}


Usage: LogManager.getInstance().log( "some message" );

Problem: It's possible in a naive implementation for one thread to preempt after the test for null but before the instance creation, so that the first thread (which has already tested for null) calls the constructor again and creates another instance.

Solution: Synchronize the getinstance method
public static synchronized LogManager getInstance() {
  if (instance == null)
   instance = new LogManager(System.out);
  return instance;
 }

Problem : Performance overhead because of the use of synchronized keyword in method signature

Solution : Just synchronize the instance creation rather than the entire method. Double-Checked Locking is widely cited and used as an efficient method for implementing lazy initialization in a multithreaded environment.
public static LogManager getInstance() {
  if (instance == null) {
   synchronized (LogManager.class) {
    if (instance == null)
     instance = new LogManager(System.out);
   }
  }
  return instance;
 }

Problem : It will not work reliably in a platform independent way when implemented in Java. The code just doesn't works in the presence of either optimizing compilers or shared memory multiprocessors. Lots of very smart people have spent lots of time looking at this. There is no way to make it work without requiring each thread that accesses the LogManager object to perform synchronization

Solution : Leave it as is. The cost of simply making the getInstance() method synchronized is not too high.

An Elegant Solution

Solution 1 : Nice way
import java.io.PrintStream;

public class LogManager {
 public static final LogManager instance = new LogManager(System.out);
 private PrintStream pStream;

 private LogManager(PrintStream out) {
  // To guard against Reflection creating the instance
  if (instance != null) {
   throw new IllegalStateException("instance already exists");
  }
  pStream = out;
 }

 public void log(String msg) {
  pStream.println(msg);
 }
}

Usage: LogManager.instance.log( "some message" );
Positives : much simpler with no performance overhead as compared to above methods as it doesn't uses the synchronized keyword.

Solution 2 : Better way
import java.io.PrintStream;

public class LogManager {
 private static final LogManager instance = new LogManager(System.out);
 private PrintStream pStream;

 private LogManager(PrintStream out) {
  // To guard against Reflection creating the instance
  if (instance != null) {
   throw new IllegalStateException("instance already exists");
  }
  pStream = out;
 }

 public static LogManager getInstance() {
  return instance;
 }

 public void log(String msg) {
  pStream.println(msg);
 }
}

Usage: LogManager.getInstance().log( "some message" );
Positives : In addition to Solution (1) advantages it gives you the flexibility to change your mind about whether the class should be a singleton without changing its API. The factory method returns the sole instance but could easily be modified to return, say, a unique instance for each thread that invokes it.

Serialization caveat :To make a singleton class that is implemented using either of the previous approaches serializable, it is not sufficient merely to add "implements Serializable" to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method. Otherwise, each time a serialized instance is deserialized, a new instance will be created. To prevent this, add this readResolve method to the Singleton class:

// readResolve method to preserve singleton property
 private Object readResolve() {
  // Return the one true LogManager and let the garbage collector
  // take care of the LogManager impersonator.
  return instance;
 }

Solution 3 : Best way

public enum LogManager {
 INSTANCE;
 private java.io.PrintStream pStream = System.out;

 public void log(String msg) {
  pStream.println(msg);
 }
}

Usage: LogManager.INSTANCE.log( "some message" );
This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.

References: http://c2.com/cgi/wiki?JavaSingleton
References: Effective java book

Tuesday, November 29, 2011

Java clone method - why to avoid





No programming language in this world is perfect and Java is no exception to it. There are quite a few bad design decisions that were made earlier and because of backward compatibility reason continue to exist even today.




One such bad design decision is related to clone method. At a first glance, it might seem like a good idea to use a method named clone to copy an object. But as we will see bellow, this is not really the case with Java’s Object.clone method.




Java has an interface called Cloneable. In principle, one should implement this interface if it is desired to make an object cloneable. The problem here is that this interface doesn’t define any methods. Instead, a clone method is defined in the Object class. The mess that it creates is implementing an interface changes the behavior of a method defined 'elsewhere'.




Moreover Object.clone is a protected method, so one must override it with a public method in order for it to be accessible – or else it should be called reflectively, but then it would get too complicated.




This is a bad design mainly because an interface should enforce you to implement some behavior in your class. Without implementing the proper method, your code shouldn’t even compile. But what Cloneable interface does is to say (in the javadoc documentation) that you should override Object.clone. Also, for the clonning to happen correctly, you would need to have your 'whole' class hierarchy 'overriding' clone. This means all super classes and all mutable objects referenced from all those classes. What about 3rd party class that doesn’t do so?





The simple way out is to consider the following two options instead of using clone method.
1. Use a copy constructor:
e.g. public MyClass(MyClass myClass) {
// initialize your fields here
}
OR
2. Create some utility method for copying the object:
public static MyClass newInstanceMyClass myClass) {
// create your object and return it here
}
The 'Effective Java' book has dealt with the topic in a more detailed way. Please refer to it for further study.

Wednesday, April 6, 2011

All about MLet


One of Java's great strengths as a software platform is its ability to dynamically load new classes. Applets and Servlets have been an integral part of Java and played a major role in Java success story. Their main capability is to load secure and platform independent code over the network at client side (applets) and server side (servlets). JMX takes advantage of this capability in management domain and the mechanism is known as Mlets.


The M-Let (short for management applet) service is a JMX agent service that allows you to load MBeans from anywhere on the network, including a local machine. The M-Let service is itself an MBean and can be managed as such. Information about MBeans to be loaded is contained in a text file called an M-Let file. This file has an XML-like syntax, but the syntax does not constitute well-formed XML. Using special tags called M-Let tags, we can encode enough information in the M-Let file that the M-Let service can locate, download the bytecode for, and instantiate MBeans.



We can also use the M-Let service, in conjunction with the MBean server, to load MBeans without the use of an M-Let file. We simply add the file's URL to the M-Let service's list of URLs that it will search when attempting to load MBeans, then call a method on the MBean server and pass the M-Let service's object name (which we created when we registered the M-Let service MBean with the MBean server) as the MBean class loader.



Architecture



Following diagram provides a brief overview of JMX architecture.





























This is how MLet fits into the overall picture.



























In progress.................