Wednesday, October 04, 2006

Eclipse quick-fixes and such

There are many refactoring and fixing tools in the eclipse environment. It really helps to have a look at them once, and find out when and how to best use them. The most important: Strg+1 Quickfix - shows a list of solutions when a warning (yellow line) or an error (red line) is selected. This also works when no error/warning is shown, but experience-based process support shows that, in most cases, the developer wants a certain task. Also: Press Strg+Space to complete any "name" - variables, functions, types, imports etc. Often, this also has some nice side effects - such as automatically importing a class when the name is completed this way. Others functions that should be looked at: (C-Control, S-Shift, A-Alt: CS+T -> "Ctrl+Shift T")

Quick fix C+1some of the possibilities offered
varname = value; // where "varname" does exist
Create field "varname"create varname as a field of the current class
Create local variable "varname"create varname as a local variable
''varname.methodname(param); // where methodname does not exist
Create Method "methodname" (in ...)create a method with the name and parameters in the appropriate class
Add Parameter in methodname (and others)Adjust the the formal parameters of methodname to conform to the given actual parameters. Includes removing, adding parameters, or creating a new method with the given ones.
Class errors:
Organize importsCS+Oremove unused or erroneous imports
Implement missing methodswhen a class does not yet implement methods required by an interface or an abstract base class
new String("Hallo"); // no variable assignment
Assign statement to new local variablecreate a new variable (usually called "string") and assign the new object.
Assign statement to new fieldcreate a new field (usually called "string")
Refactoring AS+TChange various "bad smells" in Software
RenameAS+Rrename a variable, class, method across all usages; also for package names
MoveAS+Vmove a method from one class to another, adjusting the calls (esp. for moving into member field classes, and for static methods). A delegate can be left.
Extract MethodAS+Mmake a method of the currently selected code fragment. Parameters and possibly return value are automatically determined; often other places of the same code fragment are also replaced.
Extract Local VariableAS+Lextract a local variable from the currently selected expression. Other uses of the expression in the same context are also replaced.
Inline VariableAS+IThe contrary to Extract local variable. The uses of a variable are inlined.
Extract ConstantExtracts a constant (final static) from the currently selected value, usually a "String"
Introduce ParameterIntroduces another parameter the current method, and all calls of it.
Source AS+SSource helpers
Toggle CommentCS+7Comment (out) the current lines
Override/Implement Methods
Generate Getters and Setters
Generate Constructor using Fields
Generate Constructors from Superclass
Surround withCS+Zfrom 3.2rc6(?) onwards, was part of "Source" before
Surround with try/catch

P

Invalid class exception

The following exception sometimes appears when working with serialization, especially in the case of EJB remote access (where Serializable classes, including Exceptions, are passed from server to client):

java.io.InvalidClassException: org.firebirdsql.jdbc.FBSQLException;
local class incompatible:
stream classdesc serialVersionUID = 3969094886006956637,
local class serialVersionUID = -3559440527871139169
This is always a warning that the library versions of this class differ between server and client.

Get the messages and/or the stack trace of an exception

While e.printStackTrace(); always writes onto stderr, there are also ways to get the full message of an exception, including all nested exceptions.

public static String getExceptionMessage(Throwable e) {
Throwable cause = e;
String sRet="";
do {
 sRet += "["+cause.getClass().getName() +"] ";
 sRet += cause.getMessage()+"\n";
 cause = cause.getCause();
} while (cause!=null);
return sRet;
}
to get the stack trace is even easier:
public static String getExceptionStackTrace(Throwable e) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(out);
e.printStackTrace(ps);
ps.flush();
return out.toString();
}
In some cases, it is necessary to treat certain exceptions specially. For example, the SQL exception that is raised in case of batch errors contains a special list for the various batch errors. The only way to get these errors is to check the type (of the nested exception), make an explicit cast, and list the batch errors manually.

Use Beans for data encapsulation

Use Beans for data encapsulation

Often, more than one value needs to be returned from a method. This might be especially the case with lists/collections, where each item is a tuple of various values. Solution: Use a simple java bean (that has nothing to do with Enterprise Java Beans, EJB) to encapsulate the values. A nested public static class can be used well for this. A bean is defined by Sun as a class that
  • has a default constructor (no parameters), and either
  • has only public fields, or
  • public getXXX and setXXX methods for all private/protected fields XXX.
By the naming convention, a "bean property" value can be either published as public String value; or public String getValue() {...} public String setValue(String value) {...} E.g., in the OPC database access, each data item contains a timestamp and a float value. In this case, setXXX methods are not necessary as the constructor does this work.
public class OpcDataConnection ...
public static class DataItem {
  private Date mDate;
  private float fValue;
  public Date getDate { return mDate; }
  public float getValue { return fValue; }
}
...
}
The same Beans are also used in many other cases in Java, e.g.
  • in jsp pages, such a bean can be directly accessed as property value dataItem.value, dataItem.date
  • such beans can be serialized and stored in an XMLEncoder

Log4j: Tipps and Tricks

Rolling log files w/o overwriting

When using the normal file appender, the log files grows indeterminately. When using the rolling file appender, the existing log file (of the current day) is overwritten on application restart. It should be possible to
  • use a file for each day the app is running
  • either append to the day's file, create a new file, or rename the day's file
before overwriting if the app is restarted.

Don't overwrite rolling file on startup

JBoss rolling file appender uses Append=false - this can be sensibly changed to not overwrite existing log files on startup Note: replace (, ), /) with the right XML parentheses.
(appender name="FILE" class="org.jboss.logging.appender.DailyRollingFileAppender"\)
(errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/)
 (param name="File" value="${jboss.server.log.dir}/server.log"/)
 (param name="Append" value="true"/)

 (!-- Rollover at midnight each day --)
 (param name="DatePattern" value="'.'yyyy-MM-dd"/)
[...]
It also works with the properties-based file:
log4j.appender.file.Append=true

Rename files before startup

When starting an app/server always with the same script, the log file can be moved out of the way before startup.

Rename files on startup

In the following code, the last log file is renamed to a date based pattern. Yet the pattern is based on the current date (the restart timestamp), not the original log file creation or its last entry. To use this, the logger must not be statically initialized in the main class, but after moving the file.
try {
String date = new SimpleDateFormat("yyyy.MM.dd-HH.mm").format(new Date());
// if log file already open, moving no longer possible ...
File file = new File(LOG4J_FILE_NAME+".log");
if (file.exists())
  file.renameTo(new File("LOG4J_FILE_NAME-"+date+".log"));
mLogger = Logger.getLogger(CurrentClass.class);
}
catch (IOException e) {...}
It might be possible to configure the name of the file the logger should use in the code, instead of the configuration file. This allows to set a filename manually (date pattern based) *before* the first log statement is printed and the log file thus opened. This might also require an explicit call to the ...Configurator. It will probably not work in combination with RollingFileAppender. Maybe there is no way around writing an appender (derived from RollingFileAppender) of our own?

DatedFileAppender

DatedFileAppender works in the same manner as the Tomcat FileLogger. Contrary to DailyRollingFileAppender shipping with log4j, log file names generated by DatedFileAppender always contain today's date. While this distinction seems minor, it means you can reliably copy, compress, remove or manipulate a day's log file shortly after midnight. With the DailyRollingFileAppender, a day's log file is not renamed until the first message is logged some time after midnight. http://minaret.biz/tips/datedFileAppender.html

Different log levels for stdout and file

Is there a possibility of using different log levels in log4j when logging onto stdout, and when logging into a file? The PropertiesConfigurator obviously does *not* support this, yet the xml configurator does. JBoss does this in its log4j.xml. From the log4j.dtd, no reference to such possibilities can be found. JBoss does the following in its xml configuration file:
(appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender")
(errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/)
(param name="Target" value="System.out"/)
(param name="Threshold" value="INFO"/)
[...]
This also works with the properties configuration file:
log4j.appender.stdout.Threshold=INFO

Jakarte Commons Primitives (de)

Jakarta Commons Primitives enthält eine Reihe von Collections - v.a. List, Array etc. - für die primitiven Datentypen von Java. Sicherlich schneller im Zugriff bei vielen Elementen als List und entsprechende, weil die Umwandlung Klasse <-> primitiver Type wegfällt. http://jakarta.apache.org/commons/primitives/ Aus Performance-Gründen sollte man die Objekt-Kapselungen der primitiven Datentype (also Integer, Float, Double) nur verwenden, wenn es nicht anders geht - und schon gar nicht in größeren Mengen (sprich Listen, array[] etc.).