Top Ten Myths about Java I/OElliotte Rusty HaroldJavapolisWednesday, November 13, 2002elharo@metalab.unc.eduhttp://www.cafeaulait.org/ |
Serial Ports
Parallel Ports
import javax.comm.*;
import java.util.*;
import java.io.*;
public class PortTyper {
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: java PortTyper portName");
return;
}
try {
CommPortIdentifier com =
CommPortIdentifier.getPortIdentifier(args[0]);
CommPort thePort = com.open("PortOpener", 10);
CopyThread input =
new CopyThread(System.in, thePort.getOutputStream());
CopyThread output =
new CopyThread(thePort.getInputStream(), System.out);
input.start();
output.start();
}
catch (Exception e) {
System.out.println(e);
}
}
}
class CopyThread extends Thread {
InputStream theInput;
OutputStream theOutput;
CopyThread(InputStream in) {
this(in, System.out);
}
CopyThread(OutputStream out) {
this(System.in, out);
}
CopyThread(InputStream in, OutputStream out) {
theInput = in;
theOutput = out;
}
public void run() {
try {
byte[] buffer = new byte[256];
while (true) {
int bytesRead = theInput.read(buffer);
if (bytesRead == -1) break;
theOutput.write(buffer, 0, bytesRead);
}
}
catch (IOException e) {
System.err.println(e);
}
}
}
USB
Firewire
SCSI
FileInputStream fis = new FileInputStream("symbol.txt");
InputStreamReader isr = new InputStreamReader(fis, "MacSymbol");
Here's the writeInt()
method from
java.io.DataOutputStream
:
public final void writeInt(int v) throws IOException {
OutputStream out = this.out;
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>> 8) & 0xFF);
out.write((v >>> 0) & 0xFF);
written += 4;
}
Solution:
Never use the same stream in more than one thread
Don't chain multiple filters in parallel to one underlying stream (series is OK)
import java.io.*;
import javax.swing.*;
import javax.swing.filechooser.*;
public class DirectoryChooser {
public static void main(String[] args) {
JFileChooser fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int result = fc.showOpenDialog(new JFrame());
if (result == JFileChooser.APPROVE_OPTION) {
File dir = fc.getSelectedFile();
String[] contents = dir.list();
for (int i = 0; i < contents.length; i++) {
System.out.println(contents[i]);
}
}
System.exit(0);
}
}
The following program returns various information about a file that can't exist on any platform!
import java.io.*;
public class FileInfo {
public static void main(String[] args) {
File f = new File("-What/a\bad file name?!" + '\u0000');
System.out.println("getName: " + f.getName());
System.out.println("getPath: " + f.getPath());
System.out.println("getAbsolutePath: " + f.getAbsolutePath());
System.out.println("getParent: " + f.getParent());
if (f.isFile()) {
System.out.println(f.getName() + " is a file.");
}
else if (f.isDirectory()) {
System.out.println(f.getName() + " is a directory.");
}
else {
System.out.println("What is this?");
}
if (f.isAbsolute()) {
System.out.println(f.getName() + " is an absolute path.");
}
else {
System.out.println(f.getName() + " is not an absolute path.");
}
}
}
The file separator may not be /
The current directory may not be where you expect
Absolute paths may not begin with a file separator.
public class Homework1 {
public static void main(String[] args) {
System.out.println(
"Please enter numbers, 1 to a line, -1 to quit");
double sum = 0.0;
while (true) {
int i = System.readInt();
if (i == -1) break;
sum = sum + readInt();
}
System.out.println("The sum is " + sum);
}
}
The right way:
public class Homework1 {
public static void main(String[] args) {
double sum = 0;
for (int i = 0; i < args.length; i++) {
sum = sum + Double.parseDouble(args[i]);
}
System.out.println("The sum is " + sum);
}
}
Platform dependent
Eats exceptions
Not internationalizable
import java.text.*;
public class PrettierTable {
public static void main(String[] args) {
NumberFormat myFormat = NumberFormat.getNumberInstance();
FieldPosition fp = new FieldPosition(NumberFormat.INTEGER_FIELD);
myFormat.setMaximumIntegerDigits(3);
myFormat.setMaximumFractionDigits(2);
myFormat.setMinimumFractionDigits(2);
System.out.println("Degrees Radians Grads");
for (double degrees = 0.0; degrees < 360.0; degrees++) {
String radianString = myFormat.format(Math.PI * degrees / 180.0,
new StringBuffer(), fp).toString();
radianString = getSpaces(3 - fp.getEndIndex()) + radianString;
String gradString = myFormat.format(400 * degrees / 360,
new StringBuffer(), fp).toString();
gradString = getSpaces(3 - fp.getEndIndex()) + gradString;
String degreeString = myFormat.format(degrees,
new StringBuffer(), fp).toString();
degreeString = getSpaces(3 - fp.getEndIndex()) + degreeString;
System.out.println(degreeString + " " + radianString
+ " " + gradString);
}
}
public static String getSpaces(int n) {
StringBuffer sb = new StringBuffer(n);
for (int i = 0; i < n; i++) sb.append(' ');
return sb.toString();
}
}
java PrettyTable | more
Degrees Radians Grads
000.00 000.00 000.00
001.00 000.01 001.11
002.00 000.03 002.22
003.00 000.05 003.33
004.00 000.06 004.44
005.00 000.08 005.55
006.00 000.10 006.66
007.00 000.12 007.77
008.00 000.13 008.88
009.00 000.15 010.00
010.00 000.17 011.11
011.00 000.19 012.22
012.00 000.20 013.33
013.00 000.22 014.44
014.00 000.24 015.55
015.00 000.26 016.66
016.00 000.27 017.77
017.00 000.29 018.88
018.00 000.31 020.00
019.00 000.33 021.11
020.00 000.34 022.22
Buffer all streams
Buffer all readers
Use the new I/O APIs if possible:
Readiness selection and Multiplexing
Memory mapping via MappedByteBuffer
Non-blocking modes
File locks
Scatter/Gather
Direct Channel Transfers
import java.net.*;
import java.io.*;
import java.util.*;
public class SingleFileHTTPServer extends Thread {
private byte[] content;
private byte[] header;
private int port = 80;
public SingleFileHTTPServer(String data, String encoding,
String MIMEType, int port) throws UnsupportedEncodingException {
this(data.getBytes(encoding), encoding, MIMEType, port);
}
public SingleFileHTTPServer(byte[] data, String encoding,
String MIMEType, int port) throws UnsupportedEncodingException {
this.content = data;
this.port = port;
String header = "HTTP/1.0 200 OK\r\n"
+ "Server: OneFile 1.0\r\n"
+ "Content-length: " + this.content.length + "\r\n"
+ "Content-type: " + MIMEType + "\r\n\r\n";
this.header = header.getBytes("ASCII");
}
public void run() {
try {
ServerSocket server = new ServerSocket(this.port);
System.out.println("Accepting connections on port "
+ server.getLocalPort());
System.out.println("Data to be sent:");
System.out.write(this.content);
while (true) {
Socket connection = null;
try {
connection = server.accept();
OutputStream out = new BufferedOutputStream(
connection.getOutputStream()
);
InputStream in = new BufferedInputStream(
connection.getInputStream()
);
// read the first line only; that's all we need
StringBuffer request = new StringBuffer(80);
while (true) {
int c = in.read();
if (c == '\r' || c == '\n' || c == -1) break;
request.append((char) c);
// If this is HTTP/1.0 or later send a MIME header
}
if (request.toString().indexOf("HTTP/") != -1) {
out.write(this.header);
}
out.write(this.content);
out.flush();
} // end try
catch (IOException e) {
}
finally {
if (connection != null) connection.close();
}
} // end while
} // end try
catch (IOException e) {
System.err.println("Could not start server. Port Occupied");
}
} // end run
public static void main(String[] args) {
try {
String contentType = "text/plain";
if (args[0].endsWith(".html") || args[0].endsWith(".htm")) {
contentType = "text/html";
}
InputStream in = new FileInputStream(args[0]);
ByteArrayOutputStream out = new ByteArrayOutputStream();
int b;
while ((b = in.read()) != -1) out.write(b);
byte[] data = out.toByteArray();
// set the port to listen on
int port;
try {
port = Integer.parseInt(args[1]);
if (port < 1 || port > 65535) port = 80;
}
catch (Exception e) {
port = 80;
}
String encoding = "ASCII";
if (args.length > 2) encoding = args[2];
Thread t = new SingleFileHTTPServer(data, encoding,
contentType, port);
t.start();
}
catch (ArrayIndexOutOfBoundsException e) {
System.out.println(
"Usage: java SingleFileHTTPServer filename port encoding");
}
catch (Exception e) {
System.err.println(e);
}
}
}
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.Iterator;
import java.net.*;
public class SingleFileHTTPServerNewIO {
private ByteBuffer contentBuffer;
private int port = 80;
public SingleFileHTTPServerNewIO(ByteBuffer data, String encoding,
String MIMEType, int port) throws UnsupportedEncodingException {
this.port = port;
String header = "HTTP/1.0 200 OK\r\n"
+ "Server: OneFile 1.0\r\n"
+ "Content-length: " + data.limit() + "\r\n"
+ "Content-type: " + MIMEType + "\r\n\r\n";
byte[] headerData = header.getBytes("ASCII");
this.contentBuffer = ByteBuffer.allocateDirect(data.limit() + headerData.length);
contentBuffer.put(headerData);
contentBuffer.put(data);
}
public void run() throws IOException {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverChannel.socket();
Selector selector = Selector.open();
InetSocketAddress localPort = new InetSocketAddress(port);
serverSocket.bind(localPort);
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int n = selector.select();
if (n == 0) continue;
Iterator keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
registerChannel(selector, channel, SelectionKey.OP_READ);
sendFile(channel);
}
keys.remove();
}
}
}
private void registerChannel (Selector selector, SelectableChannel channel, int ops)
throws IOException {
if (channel == null) return;
channel.configureBlocking(false);
channel.register(selector, ops);
}
private ByteBuffer buffer = ByteBuffer.wrap(new byte[4096]);
private String readHeader(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
int count;
buffer.clear();
// read the HTTP header
StringBuffer request = new StringBuffer(80);
while ((count = channel.read(buffer)) > 0) {
buffer.flip();
byte[] data = buffer.array();
request.append(new String(data, 0, buffer.limit()-1, "ASCII"));
System.out.println(request);
if (request.toString().endsWith("\n")) return request.toString();
buffer.clear();
}
if (count < 0) channel.close();
return request.toString();
}
private void sendFile (SocketChannel channel) throws IOException {
contentBuffer.clear(); // this actually resets but does not clear data
channel.write(contentBuffer);
channel.close();
}
public static void main(String[] args) {
try {
String contentType = "text/plain";
if (args[0].endsWith(".html") || args[0].endsWith(".htm")) {
contentType = "text/html";
}
FileInputStream fin = new FileInputStream(args[0]);
FileChannel in = fin.getChannel();
MappedByteBuffer input = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
// set the port to listen on
int port;
try {
port = Integer.parseInt(args[1]);
if (port < 1 || port > 65535) port = 80;
}
catch (Exception e) {
port = 80;
}
String encoding = "ASCII";
if (args.length > 2) encoding = args[2];
SingleFileHTTPServerNewIO server = new SingleFileHTTPServerNewIO(input, encoding,
contentType, port);
server.run();
}
catch (Exception ex) {
ex.printStackTrace();
System.err.println(ex);
}
}
}
import java.io.*;
public class FileCopier {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java FileCopier infile outfile");
return;
}
try {
copy(args[0], args[1]);
}
catch (IOException e) {
System.err.println(e);
}
}
public static void copy(String inFile, String outFile)
throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(inFile);
out = new FileOutputStream(outFile);
for (int b = in.read(); b != -1; b = in.read()) {
out.write(b);
}
}
finally {
try {
if (in != null) in.close();
}
catch (IOException e) {}
try {
if (out != null) out.close();
}
catch (IOException e) {}
}
}
}
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class NewFileCopier {
public static void main(String[] args) {
if (args.length != 2) {
System.err.println("Usage: java NewFileCopier infile outfile");
return;
}
try {
copy(args[0], args[1]);
}
catch (IOException e) {
System.err.println(e);
}
}
public static void copy(String inFile, String outFile)
throws IOException {
FileInputStream fin = null;
RandomAccessFile fout = null;
try {
fin = new FileInputStream(inFile);
fout = new RandomAccessFile(outFile, "rw");
FileChannel in = fin.getChannel();
FileChannel out = fout.getChannel();
MappedByteBuffer input = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
MappedByteBuffer output = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());
output.put(input);
}
finally {
try {
if (fin != null) fin.close();
}
catch (IOException e) {}
try {
if (fout != null) fout.close();
}
catch (IOException e) {}
}
}
}
No USB, Firewire support
Non-TCP/UDP IP networking
Object serialization is very slow
File system access
Java I/O is Easy and Powerful
Undeservedly difficult reputation because it's different and unfamiliar, not because it's hard.
Java I/O:
Elliotte Rusty Harold
O'Reilly & Associates 1999
$32.95
elharo@metalab.unc.edu
http://www.cafeaulait.org/
Who's Jon Johansen? http://www.eff.org/IP/DeCSS_prosecutions/Johansen_DeCSS_case/