Access:

» Debugging J2ME-based Mobile Applications

Related categories: Java | Object-oriented Technics

Adam Szarecki
Viewed: 9179 | Article date: 2006-05-11 15:18:22

Surprisingly enough, debugging applications written using the Java J2ME technology is a very problematic task. Most programmers use various emulators for that purpose, which reproduces physical devices in software; then again, this approximation is often far from perfect. When one comes across a problem which cannot be reproduced using an emulator, it presents a major technical problem. This article presents two possible solutions to this problem.

Surprisingly enough, debugging applications written using the Java J2ME technology is a very problematic task. Most programmers use various emulators for that purpose, which reproduces physical devices in software; then again, this approximation is often far from perfect. When one comes across a problem which cannot be reproduced using an emulator, it presents a major technical problem.

About the author

Adam Szarecki is an alumnus of the Faculty of Computer Science and Management of Poznañ University of Technology, presently employed at a senior programmer at the company M-navigation. The solutions presented here have come from NaviExpert – a GPS navigation system for mobile phones developed by the company. Contact: aszarecki@m-navigation.pl

The vast majority of mobile devices, especially mobile phones not based on the Symbian operating system, doesn't allow direct access to standard output streams System.out and System.err, commonly referred to as "the console", which we could use to watch messages produced by our applications. This article presents two possible solutions to this problem.

Architecture

Let us begin by defining a superclass called Log, which will be used to broadcast messages. Depending on its configuration, it will be possible to send those messages to standard output or redirect them to an arbitrary destination for storage or presentation in a different form. Our architecture will take advantage of polymorphism, isolating the logic of filtering and formatting messages (placed in the superclass) from their physical storage or presentation (which will be implemented in subclasses).

In order for the logging to be easy to use and save memory, public methods used for logging are static. They redirect method calls to a single instance of one of the subclasses of Log (the software patterns of: delegation and singleton objects).

Listing 1. Source code of the class Log.java

package com.naviexpert.util;
/**
* J2ME log class
*/

public class Log {
/** A monitor used for thread synchronisation. */
private final static Object monitor = new Object();
/** Class {@link Log} used for logging */
public static Log logger = null;
/**
* logger class time initialisation.
*/

public static long loggerInitializationTime = System.currentTimeMillis();
/**
* Static initialisation block creating logger class.
* Change on subclass now causes exchange logger way into
* implemented by subclass.
*/

static {
try {
logger = new Log();
} catch (Throwable t) {
logger = new Log();
}
}
/** Login flag */
public static final boolean LOGGING_ON = true;
/** flag switching on login synchronisation */
public static final boolean SYNCHRONIZED_LOG = true;
/* Definition of login level (filter). */
public final static int DEBUG = 0;
public final static int INFO = 1;
public final static int WARN = 2;
public final static int ERROR = 3;
/** message filter level */
private static int filterAt = DEBUG;
/** Static string table related to login level. */
protected final static String[] levels = new String[] { "[debug] ", "[info] ", "[warn] ", "[error] " };
/** Classical development of this class is forbidden */
protected Log() {
}
/**
* Sending message bundled with thread.
*
*
@param level message level
*
@param message message content
*
@param exception Thread bundled with message or
* <code>null</code>.
*/

public static void log(int level, String message, Throwable exception) {
if (LOGGING_ON) {
if (level < filterAt) return;
if (SYNCHRONIZED_LOG) {
synchronized (monitor) {
logger.doLog(level, message, exception);
}
} else {
logger.doLog(level, message, exception);
}
}
}
protected String getSecondsSinceLogInit() {
return String.valueOf((System.currentTimeMillis() - loggerInitializationTime) / 1000);
}
/**
* Internal method. Standard usage
* <code>System.out</code>, to use it in different way
* messages should be overloaded.
*
*
@param level Message level
*
@param message Message content
*
@param exception Thread bundled with message or
* <code>null</code> if no thread bundled with message.
*/

protected void doLog(int level, String message, Throwable exception) {
System.out.print(getSecondsSinceLogInit() + ": ");
try {
System.out.print(levels[level]);
} catch (ArrayIndexOutOfBoundsException e) {
throw new Error("Blad logowania");
}
System.out.print(message);
if (exception != null) {
exception.printStackTrace();
}
System.out.println();
}
/**
* Wysy
*
*
@param level Level of the message to be logged.
*
@param message The message to be logged.
*/

public static void log(int level, String message) {
log(level, message, null);
}
/* Support methods. */
public static void debug(String message) {
log(DEBUG, message);
}
public static void debug(String message, Throwable t) {
log(DEBUG, message, t);
}
public static void info(String message) {
log(INFO, message);
}
public static void warn(String message) {
log(WARN, message);
}
public static void warn(String message, Throwable t) {
log(WARN, message, t);
}
public static void error(String message) {
log(ERROR, message);
}
public static void error(String message, Throwable t) {
log(ERROR, message, t);
}
/**
* Messages filter level settings.
*/

public static void setFilterAt(int level) {
filterAt = level;
}
/**
* Return messages filter level.
*/

public static int getFilterAt() {
return filterAt;
}
/**
* Close login class.
*/

public static void shutdown() {
logger.shutdownInstance();
}
/**
* Close instanse.
*/

public void shutdownInstance() {
}
}

One of the advantages of this solution is sane size of final code, encompassing only classes which really are used in the application (automatic removal of classes without references from code is referred to by the term obfuscation).

Listing 2. Source code of the class LogServerListener.java – a server receiving logging messages

package com.naviexpert.util;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
import java.io.File;

public class LogServerListener {
public final static int portNr = 9999;
public static void println(Writer os, String arg) {
System.out.print(arg);
System.out.flush();
try {
os.write(arg);
os.flush();
} catch (IOException e) {
// ignorujemy
System.out.print("I/O exception: " + e.toString());
}
}

public static void main(String[] args) throws UnknownHostException, IOException {
if (args.length != 1) {
System.out.println;
return;
}
if (new File(args[0]).isDirectory() == false) {
System.out.println("Wrong argument � directory not exist:" + args[0]);
return;
}
final String dir = new File(args[0]).getPath() + File.separatorChar;
ServerSocket socket = new ServerSocket(portNr);
System.out.println("Port:" + portNr);
int count = 0;
while (true) {
final Socket client = socket.accept();
new Thread() {
final PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(dir
+
String.valueOf(new Date().getTime()))));
public void run() {
try {
System.out.println("Save history in dir:" + dir);
println(out, "nn---------------------------nn");
InputStream is = client.getInputStream();
byte[] buf = new byte[1024 * 4];
while (true) {
int bytes = is.read(buf);
if (bytes == -1) break;
println(out, new String(buf, 0, bytes));
}
} catch (IOException e) {
try {
client.close();
} catch (IOException e1) {
}
}
}
}.start();
count++;
}
}
}
Page: 1 2
Buy article Buy subscription
Buy now add to cart
add to cart
Standard price: 2€/$3 Standard price: 25€/$30
Buy article for as little as (2€/$3) each allow access to individual articles. Buy a full access to our Software Developers's Journal archive portal. You will be able to read the articles from all archive issues from year 2005 and 2006. For just 25€/$30 you get unrestricted access to the entire website for the whole year.
SDJhakin9

.SDJ Users:


.:Login
.:Password

[Register]
[Forgotten your password?]

...Shopping Cart

sum: 0 €
Choose currency:

...Topics

...Advertisement

www.acunetix.com www.verifysoft.com

...Conferences




...Print Edition Archive

...Affiliate Program



 

 

Subscribe | Contact Us | Newsletter | Privacy policy | Regulations | See all issues | About SDJ
Copyright C 2006 by Software Developer's Journal. All rights reserved.