Java Newsletter by Glen McCluskey - Issue 15
Issue #015
March, 1997
Contents
- Java Test Suites
- JDK 1.1
- Introduction to Java Security Part 2 - Verification
- Books on Java
- Comparing C/C++ and Java Part 15 - Bitfields
- Introduction to AWT Programming Part 2 - Another Example
JAVA TEST SUITES
I am currently working on developing commercial test suites for several of the Java API packages (libraries). The first two of these will cover reflection (java.lang.reflect) and utilities (java.util).
These suites exhaustively test the features of the API. For example, the one on reflection is about 6000 lines of code.
If you have an interest in obtaining these products, please contact me (glenm@glenmccl.com).
JDK 1.1
Javasoft has released the official version of the Java Development Kit 1.1. You can download it at:
http://java.sun.com/products/JDK/1.1
JDK 1.1 contains many new features, such as reflection and serialization.
INTRODUCTION TO JAVA SECURITY PART 2 - VERIFICATION
We saw last time how the Java virtual machine model insulates a Java program from hardware, and how this is both desirable and undesirable.
In this issue we'll talk a bit about verification, another aspect of security. A java class is compiled into platform-independent ".class" files containing byte streams. These streams contain the actual program codes (bytecodes, constants, debugging information, and so on).
Suppose that I have the hello program:
public class hello {
public static void main(String args[])
{
System.out.println("Hello World");
}
}
and I compile it:
$ javac hello.java
resulting in a "hello.class" file of 461 bytes (in JDK 1.1). And I'm feeling malicious and decide to tweak one of the bytes:
import java.io.*;
public class tweak {
public static void main(String args[])
{
int offset = Integer.parseInt(args[0]);
try {
RandomAccessFile raf =
new RandomAccessFile("hello.class", "rw");
raf.seek(offset);
raf.writeByte(97);
raf.close();
}
catch (Throwable e) {
System.err.println("*** exception ***");
}
}
}
by saying:
$ javac tweak.java
$ java tweak 0
thereby writing the byte "97" into location 0 in "hello.class". Obviously, this is not how Java was intended to be used. What will happen?
It turns out that the first four bytes of a Java .class file must be the hex value "0xCAFEBABE", and writing 0 into one of these will invalidate the file. If I then try to run the program:
$ java hello
I will get an immediate error. This particular feature is fairly common in program binaries and often goes by the name of "magic number".
When a Java program is run, the interpreter first invokes the Java Verifier. The Verifier checks the magic number along with other properties of the .class file, including:
- stack overflows
- no illegal data conversions
- access to public/private/protected
- proper format for the .class file
- methods have appropriate arguments
There are many other checks done by the Verifier on .class files. This list is merely illustrative. Verifying a class not only improves security, but speeds up actual bytecode interpretation because the checks don't have to be repeated.
An interesting book that goes into detail on Java security is "Java Security - Hostile Applets, Holes, and Antidotes", by Gary McGraw and Edward Felten, published 1997 by Wiley for $20.
We will be discussing security further in future issues.
BOOKS ON JAVA
If you have Web access, there is a page that has a comprehensive list of Java books that have been published or are in the works:
http://lightyear.ncsa.uiuc.edu/~srp/java/javabooks.html
Several hundred books are listed.
COMPARING C/C++ AND JAVA PART 15 - BITFIELDS
In C and C++, it's possible to have usage such as:
struct A {
int x : 5;
int y : 2;
int z : 1;
};
with the idea being that the data fields take the indicated number of bits. In theory this particular structure would require 8 bits or one byte per instance. In practice, because of padding and packing issues, each instance might take more than a byte.
Bitfields are sometimes used for interfacing to hardware, for example where specific bits in a machine register have particular meaning.
Java has no bitfields. As we discussed in the previous issue of the newsletter, the language is pitched at a somewhat higher level than C and C++, and there's no direct way to manipulate hardware resources.
And Java has a variety of types, such as boolean and byte, that support efficient representations of data.
INTRODUCTION TO AWT PROGRAMMING PART 2 - ANOTHER EXAMPLE
In the last issue we started a series on AWT (Abstract Window Toolkit) programming. We presented an example of writing a "more" program, one that can be used to page through text from a file. In this issue we'll show another more complicated and powerful way of achieving the same end.
Before diving into this, a few general comments are in order. This code is written against JDK 1.1. It gives some "deprecation" warnings, about methods which have changed in the JDK. As such, the code needs to be updated, but doing so would make it not work with earlier versions. This is simply something that we have to put up with in an evolving language.
Also, this code reveals a bug in the JDK 1.1 implementation for Windows NT. If you compile this program and run it, you will notice that scroll bars are flaky. This is a known bug that is supposed to be fixed in a bug fix release of 1.1.
Finally, we are getting into an area that is perhaps not as stable as some other parts of Java, and still evolving (as is my understanding of it). Some aspects of the example below, notably layout manager usage, are a bit tricky.
As you will recall, the previous version of the "more" program had one big deficiency, namely, that it read in a whole file before displaying it. This is not acceptable for a very large file, especially with relatively slow I/O. We've fixed that problem, and to do so, set up our own text windowing scheme with scroll bars which we manage.
Here is the actual code. If you scan down the left side, you can see interspersed comments starting with "//" in the left margin.
import java.awt.*;
import java.io.*;
// a text area in the window
// This is the class used to manage a text area, with scroll bars.
// It is passed a RandomAccessFile object to read lines from, and
// reads them on demand only.
// A Canvas is an AWT object suitable for displaying text on.
class Text extends Canvas {
private static final int LISTSIZ = 16;
private String list[] = null;
private int scnt = 0;
private int currpos = 0;
private RandomAccessFile raf = null;
private boolean ateof = false;
// initialize
void init(RandomAccessFile r)
{
if (raf != null) {
try {
raf.close();
}
catch (Throwable e) {
System.err.println("close err");
System.exit(1);
}
}
raf = r;
ateof = false;
currpos = 0;
scnt = 0;
list = new String[LISTSIZ];
}
// We need to detab Strings that are displayed, because tabs
// are handled in a funny way by Graphics.drawString().
// detab a String
private static String detab(String s)
{
StringBuffer sb = new StringBuffer();
int len = s.length();
int pos = 0;
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (c == '\r' || c == '\n') {
}
else if (c == '\t') {
do {
sb.append(' ');
pos++;
} while (pos % 8 != 0);
}
else {
sb.append(c);
pos++;
}
}
return sb.toString();
}
// This is the internal list used to store lines to be displayed.
// We could also use java.util.Vector to manage this list, and
// System.arraycopy() to copy the list.
// add a String to the list
void add(String s)
{
if (scnt == list.length) {
String x[] = new String[list.length * 3 / 2];
for (int i = 0; i < scnt; i++)
x[i] = list[i];
list = x;
}
list[scnt++] = detab(s);
}
// Called by the handleEvent() method below.
// scroll up or down a line
void scroll(int dir)
{
currpos += dir;
if (currpos < 0)
currpos = 0;
repaint();
}
// This is the paint() method, used to draw lines in the text window.
// We use FontMetrics to determine how tall a character is, and display
// as many lines as will fit.
// paint
public void paint(Graphics g)
{
Dimension d = size();
int startpos = More.HEIGHT - d.height;
int pos = startpos;
int sp = g.getFontMetrics().getHeight();
int i = currpos;
while (pos + sp < d.height) {
while (i >= scnt && !ateof) {
String s = null;
try {
s = raf.readLine();
}
catch (Throwable e) {
System.err.println("I/O err");
System.exit(1);
}
if (s == null)
ateof = true;
else
add(s);
}
if (i >= scnt)
break;
g.drawString(list[i], 5, pos);
i++;
pos += sp;
}
}
}
// We use a Panel to contain the text area, along with scroll bars.
// A Panel is an AWT object that can contain other objects.
// a Panel for the text area, with scroll bars
class Panel_ta extends Panel {
private Scrollbar vbar = null;
private Text t = null;
// constructor
Panel_ta(Text ta)
{
vbar = new Scrollbar(Scrollbar.VERTICAL);
// Put the text area in the center, and the scroll bar on the
// "East" side.
setLayout(new BorderLayout(0, 0));
add("Center", ta);
add("East", vbar);
t = ta;
}
// handleEvent() is called for actual scrolling.
// handle scrolling
public boolean handleEvent(Event e)
{
if (e.target == vbar) {
switch (e.id) {
case Event.SCROLL_LINE_UP:
t.scroll(-1);
break;
case Event.SCROLL_LINE_DOWN:
t.scroll(1);
break;
}
return true;
}
else {
return super.handleEvent(e);
}
}
}
// The actual "More" class.
public class More extends Frame {
private Button b1 = new Button("New File");
private Button b2 = new Button("Exit");
private Text ta = new Text();
static final int WIDTH = 600;
static final int HEIGHT = 450;
// A dialog invoked when the user selects "New File".
// open a new file
private void new_file()
{
FileDialog fd = new FileDialog(this, "Open File",
FileDialog.LOAD);
fd.show();
if (fd.getDirectory() == null ||
fd.getDirectory().equals(""))
return;
if (fd.getFile() == null || fd.getFile().equals(""))
return;
if (fd.getFile().equals("*.*"))
return;
String fn = fd.getDirectory() + File.separator +
fd.getFile();
load(fn);
}
// Called when buttons are selected.
// handle an action
public boolean action(Event e, Object arg)
{
if (e.target instanceof Button) {
if (e.target == b1) {
new_file();
return true;
}
if (e.target == b2) {
dispose();
System.exit(0);
return true;
}
return false;
}
else {
return false;
}
}
// Load a new file in.
// load a file
private void load(String fn)
{
try {
RandomAccessFile raf =
new RandomAccessFile(fn, "r");
ta.init(raf);
}
catch (Throwable e) {
System.err.println("open err");
System.exit(1);
}
ta.repaint();
}
// Constructor for More.
// constructor
public More(String title, String fn)
{
super(title);
resize(WIDTH, HEIGHT);
// Set a layout manager for the buttons, flowing across the screen.
Panel p1 = new Panel();
p1.setLayout(new FlowLayout(FlowLayout.CENTER,50,0));
p1.add(b1);
p1.add(b2);
// Set a fixed-width font, size 12.
ta.setFont(new Font("Courier", Font.PLAIN, 12));
Panel p2 = new Panel_ta(ta);
// Set a layout manager for the buttons at the top, and the text area
// at the bottom. GridBagLayout is the most complicated and powerful
// of the layout managers. You can specify cell positions, cell
// weights, and so on. Each object to be laid out has its constraints
// set for it.
// In this case, we give the top row of buttons a weight of 1, and the
// text area a weight of 7, so the text area will take 7/8 of the total
// window.
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
setLayout(gbl);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.CENTER;
gbl.setConstraints(p1, gbc);
gbc.gridy = 1;
gbc.weighty = 7;
gbl.setConstraints(p2, gbc);
add(p1);
add(p2);
load(fn);
// Actually display the window.
show();
}
// Driver main() method.
// driver
public static void main(String args[])
{
Frame f = new More("More", args[0]);
}
}
This example is fairly complicated, but illustrates several important points about the AWT. There are other ways to approach this problem. Another feature we could add would be one to search through the file for a specified string, and display the line it's found on.
ACKNOWLEDGEMENTS
Thanks to Jay Burgess, Thierry Ciot, Irv Kanode, Mike Paluka, Anand Ramachandran, Srihari Sampathkumar, Jason Sharp, and Bob Shore for help with proofreading.
SUBSCRIPTION INFORMATION / BACK ISSUES
To subscribe to the newsletter, send mail to majordomo@world.std.com with this line as its message body:
subscribe java_letter
Back issues are available via FTP from:
rmi.net /pub2/glenm/javalett
or on the Web at:
There is also a C++ newsletter. To subscribe to it, say:
subscribe c_plus_plus
using the same majordomo@world.std.com address.
-------------------------
Copyright (c) 1997 Glen McCluskey. All Rights Reserved.
This newsletter may be further distributed provided that it is copied in its entirety, including the newsletter number at the top and the copyright and contact information at the bottom.
Glen McCluskey & Associates
Professional Computer Consulting
Internet: glenm@glenmccl.com
Phone: (800) 722-1613 or (970) 490-2462
Fax: (970) 490-2463
FTP: rmi.net /pub2/glenm/javalett (for back issues)
Web: http://rainbow.rmi.net/~glenm