Skip to content
accelerando

Tag Archives: Java

A fistfull of readers

23-Oct-07

After presenting a working InputStream on a ByteBuffer, i have to more readers for you out there.

First, the StringBufferReader to efficient read data from a StringBuffer. One can use new java.io.StringReader(sb.toString()) but that would convert the whole StringBuffer (sb) to a string, loosing a whole lotta memory if the string is just big enough. If you can assure that the StringBuffer don’t need to be modified, use the following code:

import java.io.IOException;
import java.io.Reader;
 
public class StringBufferReader extends Reader {
	private int pos;
	private final StringBuffer sb;	
	private boolean closed;
 
	public StringBufferReader(final StringBuffer sb) {
		this.sb = sb;
		this.pos = 0;
		this.closed = false;
	}
 
	@Override
	public void close() throws IOException {
		this.closed = true;
	}
 
	@Override
	public int read(char[] cbuf, int off, int len) throws IOException {
		if(closed)
			throw new IOException("Reader is closed");
		int _len = Math.min(len, sb.length() - pos);		
		sb.getChars(pos, pos + _len, cbuf, off);
		pos += _len;
		return _len == 0 ? -1 : _len;
	}
}

It uses no intermediate buffer.

In the same context i stumbled upon the Byte Order Mark (BOM) in some UTF8 files (especially ones that were created with tools under Microsoft Windows).

The InputReaders and Streams in the JRE don’t skip the BOM, neither does the org.dom4j.io.SAXReader so parsing of such XML files or strings fails with something like “Content not allowed in prolog”. Enter my simple BOMSkippingReader:

import java.io.IOException;
import java.io.Reader;
 
/**
 * This reader skips a possible Byte Order Marker at the 
 * start of UTF8 files which java doesn't.
 * @author michael
 *
 */
public class BOMSkippingReader extends Reader {
	private final Reader decorated;	
	private final boolean rewrite;
	private final int firstchar;
	private int pos = 0;
 
	public BOMSkippingReader(final Reader decorated) throws IOException {
		this.decorated = decorated;
 
		this.firstchar = decorated.read();						
		this.rewrite = firstchar != 65279;			
	}
 
	@Override
	public void close() throws IOException {
		decorated.close();
	}
 
	@Override
	public int read(char[] cbuf, int off, int len) throws IOException {					
		int redone = 0;					
		while(rewrite && pos < 1) {
			cbuf[off + pos++] = (char) firstchar;
			++redone;
		}
		return redone + decorated.read(cbuf, off + redone, len - redone);				
	}
}

If there’s a BOM in the underlying reader, it’s skipped, otherwise written back to the reader. Works well for me and the small while loop doesn’t have much impact on the performance in my app.

Java: Creating a Stream on a ByteBuffer

18-Oct-07

This example is plain wrong. The InputStream will cause an endless loop as it always returns a value >= 0. Working code as follows:

private static InputStream _newInputStream(final ByteBuffer buf) {
	return new InputStream() {
		public synchronized int read() throws IOException {				
			return buf.hasRemaining() ? buf.get() : -1;
		}
 
		public synchronized int read(byte[] bytes, int off, int len) throws IOException {			
			int rv = Math.min(len, buf.remaining());				
			buf.get(bytes, off, rv);
			return rv == 0 ? -1 : rv;
		}
	};
}

JDBC: Get autogenerated keys on a Oracle DB

09-Oct-07

With the current and latest Oracle JDBC Drivers it’s possible to retrieve one automatically generated key based on a sequence or any other arbitrary value (autogenerated-keys or identity columns in other databases).

Certainly it isn’t as simple as just use using Statement.html#getGeneratedKeys() as it simply returns an emtpy resultset.

After browsing around, i saw that java.sql.Connection can prepare a statement with a flag to return generated keys like so:

connection.prepareStatement("INSERT INTO FOOBAR VALUES(id.nextval, 1), Statement.RETURN_GENERATED_KEYS);

(By the way, it’s always a good idea to use prepared statements from a performance point of view as they can be reused)

Anyway, it wouldn’t be an Oracle product if something is different like any other products and i still was left alone in the dark with an empty result set.

Follow the path to enlightment:

final String sql = "INSERT INTO foobar(id, b) VALUES (id.nextval, ?)";
stmt = connection.prepareStatement(sql,new String[]{"ID"});			
stmt.setString(1, "bar");
stmt.execute();		
rs = stmt.getGeneratedKeys();
rs.next();
rv = rs.getInt(1);

Telling oracle which column contains the generated value does the trick. It’s really well hidden on their website. Be aware, you cannot refer to the returned keys by name, you need to address them 1 based.

Be aware that this doesn’t work inside a 9.2.x.x or 10.2.x.x Oracle Database as a Java Stored Procedure. Either the driver isn’t JDBC 3 (9.2) or the methods are not supported (10.2). You can work around this problem with 2 statements, first select id.nextval from somewhere, then execute your insert. Lame, but i didn’t find any other solution.

Ran on the client side with the latest JDBC from Oracle on the other hand works just fine.

The Pan-Computer-Programming-Language Conference

05-Oct-07

Ruby (grabbing the microphone): Um so yeah I’d just like to kick this bad boy off by saying that THE REST OF YOU SUCK A**!!! Yeah, I said it! The A-word! A**! Oh yeah! Boom, baby! Woo! Ruby FTW!

Read the whole conference here, found at the gay bar.

On Java Threads: A fairytale of a tutorial

12-Jul-07

I always thought that the Java Thread API is something… strange. If you work in a frontend application, things like running long-running tasks in the back without having the GUI ugly frozen and not responding should be somewhat simpler.

SwingWorker has been around for quite a time but made it just recently into the core API (Java6). Furthermore i don’t think that it’s the right thing for performing enduring tasks like checking for mail and pushing a result with a second thread into a database, for example.

Don’t get me wrong, i use SwingWorker quite often, but it didn’t fit my needs and furthermore, i wanted to learn more about Java Threads.

My goal / task was a little daemon that regularly checks an email account and a samba share for some files, load them into an Oracle Database and executes a longer running db procedure. The checking should be suspendable and stoppable independently, the configuration should be reloadable.

I throw some interfaces and abstract classes at the vm and boom, it was that simple ;)

Things i’ve learned:

  • Always start the GUI in it’s own thread, never use the main thread. Sure, most programs will work fine, but it can get confusing. Use SwingUtilities to do so.
  • A thread once terminated is not reusable. Never ever. So don’t interrupt them if you plan on resume them later.
  • Know the primitives, i.e. build in locks (synchronized and wait())
  • Know the task scheduling frameworks (Executors and ExecutorServices)
  • Read the tutorials here, here and maybe here

The following demo can start 7 producers and one consumer, both are synchronized via a BlockingQueue (i actually used a SynchronousQueue at work, as the files must only be removed if they were taken by the db). Both the producers and consumers can be suspended, resumed and stopped. I never ever will start a thread by hand again if not necessary. The threads are managed by an ExecutorService.

For the tasked mentioned above this thing works fine. If anyone comes up with a better idea, let me know.

I had fun to write it, maybe you have fun to read. Be aware, the program is not a good example of organizing classes, i put everything in one file just for the sake of being a demo.

But apart from that, the demo could serve as an example of what came to Java with Java 5 and 6 as i use a lot of generics, enums and enhanced for loops, in case you haven’t seen this.

To compile and run the stuff you need at least a Java5 JDK (get it here, download this file JThreadDemo.zip, unzip it and type

javac snow/white/JThreadDemo.java
java snow.white.JThreadDemo

If you read this, i’ll guess you’re familiar with the JDK, java packages and the whole crap.

Get the whole fun after the click:

More…

Close
E-mail It