Advanced Java Programming Workbook
Advanced Java Programming Workbook
Programming
Advanced Java
Programming
Student Workbook
Advanced Java Programming
Contents
Chapter 1 - Course Introduction ............................................................................................................. 11
Course Objectives ............................................................................................................................ 12
Course Overview .............................................................................................................................. 14
Using the Workbook ......................................................................................................................... 15
Suggested References ....................................................................................................................... 16
Constructors ..................................................................................................................................... 70
Fields ................................................................................................................................................ 72
Methods ........................................................................................................................................... 74
Exception Handling and Reflection .................................................................................................... 76
JavaBeans ......................................................................................................................................... 78
Dynamic Programming ...................................................................................................................... 80
Labs ................................................................................................................................................. 82
Index..................................................................................................................................................... 583
Course Objectives
Write Java programs that use advanced features of JDBC to interact with a
relational database.
Use activation and dynamic class loading within your RMI applications.
Bind and lookup objects in a naming service using the Java Naming and
Directory Interface (JNDI).
Access XML content with the Java API for XML Processing (JAXP).
Integrate legacy C/C++ code into your Java programs using the Java Native
Interface (JNI).
Course Overview
Classroom Environment:
¾ DBMS Server.
This workbook design is based on a page-pair, consisting of a Topic page and a Support page. When you
lay the workbook open flat, the Topic page is on the left and the Support page is on the right. The Topic
page contains the points to be discussed in class. The Support page has code examples, diagrams, screen
shots and additional information. Hands On sections provide opportunities for practical application of key
concepts. Try It and Investigate sections help direct individual discovery.
In addition, there is an index for quick look-up. Printed lab solutions are in the back of the book as well as
on-line if you need a little help.
third ()level
Whenpoints.
the servlet is unloaded, the container calls the destroy()
public void service(ServletRequest request,
ServletResponse response) throws ServletException, IOException
method. {
...
// Write the document
As with Java’s finalize() method, don’t count on this being
called.
Callout boxes point out
[Link]("This servlet was born on " + [Link]());
[Link]("It is now " + [Link]());
[Link](config);
Suggested References
Fisher, Maydene, Jonathan Bruce, and Jon Ellis. 2003. JDBC API Tutorial and Reference: Universal
Data Access for the Java2 Platform, Third Edition. Addison-Wesley, Reading, MA.
ISBN 0321173848.
Flanagan, David. 2005. Java in a Nutshell, Fifth Edition. O'Reilly & Associates, Sebastopol, CA.
ISBN 0596007736.
Freeman, Elizabeth, et al. 2004. Head First Design Patterns. O'Reilly & Associates, Sebastopol, CA.
ISBN 0596007124.
Gamma, Erich, et al. 1995. Design Patterns. Addison-Wesley, Reading, MA. ISBN 0201633612.
Gordon, Rob. 1998. Essential JNI: Java Native Interface. Prentice Hall, Upper Saddle River, NJ.
ISBN 0136798950.
Harold, Elliotte Rusty. 2004. Java Network Programming, Third Edition. O'Reilly & Associates,
Sebastopol, CA. ISBN 0596007213.
Hitchens, Ron. 2002. Java NIO. O'Reilly & Associates, Sebastopol, CA. ISBN 0596002882.
Horstmann, Cay S. and Gary Cornell. 2004. Core Java 2, Volume II: Advanced Features, Seventh
Edition. Prentice Hall, Upper Saddle River, NJ. ISBN 0131118269.
Oaks, Scott. 2001. Java Security, Second Edition. O'Reilly & Associates, Sebastopol, CA.
ISBN 0596001576.
[Link]
Objectives
What is Serialization?
The other Java program will re-create the object when reading it.
astronaut:Person
id = 1
Serialization
¬ í \0 005 s r \0 006 P e r s o n E ý
b ÿ T 235 ê h 002 \0 004 I \0 002 i d L \0
\t b i r t h D a t e t \0 020 L j a
v a / u t i l / D a t e ; L \0 004
n a m e t \0 022 L j a v a / l a n
g / S t r i n g ; L \0 005 t i t l
e q \0 ~ \0 002 x p \0 \0 \0 001 s r \0 016
j a v a . u t i l . D a t e h j
201 001 K Y t 031 003 \0 \0 x p w \b ÿ ÿ þ
Þ r ¯ - 200 x t \0 016 N e i l A r
m s t r o n g t \0 \t A s t r o n
a u t
Serializable Objects
package [Link];
public interface Serializable {}
¾ Implementing the interface simply marks your class for special treatment
by the Virtual Machine.
This Person class is an example of a Serializable class. The toString() and equals() methods are included
to compare original objects with restored objects.
[Link]
...
public class Person implements Serializable {
private String name;
private String title;
private Date birthDate;
private int id;
public Person() {
}
public Person(String nm, String ti, String shortBirthDate, int i) {
name = nm;
title = ti;
id = i;
try {
SimpleDateFormat df = new SimpleDateFormat("M/d/yyyy");
birthDate = [Link](shortBirthDate);
}
catch (ParseException e) {
[Link]("Parsing error: " + shortBirthDate);
birthDate = new Date();
}
}
public boolean equals(Object obj) {
if (! (obj instanceof Person)) {
return false;
}
Writing an Object
Marshalling an object involves dissecting the object into its component elements.
Since reference data (object members) must also be sent, they must implement
Serializable.
The writeObject() method takes an Object, not a Serializable. This means that attempting to write a non-
serializable object will not result in a compiler error, but in a NotSerializableException, which will
be caught in the IOException catch, but will not be very descriptive.
[Link]
import [Link];
import [Link];
import [Link];
Try It:
Compile and run [Link]. You can look at the resulting [Link] file and see if you recognize
any of the data.
Reading an Object
¾ Downcast the object to the appropriate class, making sure to catch the
ClassCastException which may result.
¾ If the object has been read from the stream already, then a reference to the
existing object is returned.
This is then used to load the class and verify the version.
[Link]
import [Link];
import [Link];
import [Link];
ObjectInputStream in = null;
try {
in = new ObjectInputStream(
new FileInputStream("[Link]"));
Person p = (Person) [Link]();
[Link](p);
}
catch (ClassCastException e) {
[Link]("Error casting object to a Person");
}
catch (ClassNotFoundException e) {
[Link]("Class not found"); StreamCorruptedException
} extends IOException.
catch (IOException e) {
[Link]("Error reading object: " + [Link]());
}
finally {
try {
[Link]();
}
catch (IOException e) {
[Link]([Link]());
}
}
}
}
Try It:
Compile and run [Link] to load the person.
Handling Exceptions
[Link]
...
public class BadBatchProcessor implements Runnable, Serializable {
private ArrayList<Runnable> jobs;
private ListIterator<Runnable> iterator;
While serializing the BadBatchProcessor, an Exception will be thrown due to the non-serializable
ListIterator.
Try It:
Run BadBatchProcessor once to create the serialized object with the Exception. Then run it again to see
what happens when the object is read.
Customizing Serialization
Use the transient modifier to specify that a data member should not be
serialized.
¾ The transient data will not be initialized when the object is read.
Use the transient modifier either for data which is temporary, such as mouse coordinates, or for data
which cannot be successfully serialized. While the Serializable interface does not require any
implementation, it is the developer's responsibility to ensure that the class and all of its data members
are, in fact, Serializable. Failure will result in a runtime exception.
[Link]
...
public class BatchProcessor implements Runnable, Serializable {
private ArrayList<Runnable> jobs;
private transient ListIterator<Runnable> iterator; Since the ListIterator
is not Serializable,
public BatchProcessor(ArrayList<Runnable> jobs) { declare it transient.
[Link] = jobs;
iterator = [Link]();
}
Try It:
BatchProcessor has a main() method. The first time you run it, it creates a new batch of test jobs and runs
for a couple of seconds. Each time you run it after that it will continue where it left off.
Controlling Serialization
Classes that need to completely control the serialization process can implement
[Link], which extends Serializable.
This includes fields from the class and all its superclasses.
¾ The ObjectStreamClass and internal handle are used in the same way as
they are for Serializable classes.
Use Externalizable when you need more control than readObject() and
writeObject() provide.
Customer is a subclass of Person, so it inherits Person's serialization behavior. For privacy, we want to
leave the personal information out of the serialized objects. The easiest way to do this without changing
Person is to use Externalizable.
[Link]
...
public class Customer extends Person implements Externalizable {
private int accountNumber;
Try It:
Test Customer with WriteCust and ReadCust.
Versioning
¾ By default, this long value is calculated from the name and signature of
the class and its fields and methods using the Secure Hash Algorithm.
¾ You can provide your own public static final long serialVersionUID
for backward compatibility.
Use the serialver utility (before you change the class) to find out
the old version ID: serialver Person.
You must ensure that the changes are both forwards and backwards compatible.
The new class must handle default values for fields missing from
old objects.
The old class will ignore the unknown fields in new objects.
The old class might rely on non-default values from the fields
missing in new objects.
[Link]
...
public class Person implements Serializable {
public static final long serialVersionUID = 5043296006500641384L;
...
public String toString() {
SimpleDateFormat df = new SimpleDateFormat("MMM d, yyyy");
StringBuilder result = new StringBuilder();
[Link](title).append(" ").append(name);
[Link](" born ").append([Link](birthDate));
[Link](" id ").append(id);
if (quote == null) {
[Link](" old class version: no quote specified");
} Here we handle objects serialized
else { under the old version of the class.
[Link](" quote ");
[Link]('"').append(quote).append('"');
}
return [Link]();
}
...
}
Try It:
Make sure you have already run WriteObj with the original version of Person.
Now run WriteObj to create a new serialized object. Restore [Link] from the backup, then compile
it and run ReadObj to view the new object with the old class.
Labs
Write an Employee class with appropriate fields, including a hireDate. Write a Department class
which has an Employee for the manager. Write an application which creates a Department object
and writes it to a file. (Hint: Use SimpleDateFormat to create a hire date.)
(Solution: [Link], [Link], [Link])
Write an application that reads a Department object from a file and displays it.
(Solution: [Link])
Modify the solution to to read from a file that doesn't contain a serialized object.
(Solution: [Link], [Link])
In the next set of exercises you will modify your Employee class to create a new version. You should save a
copy of [Link] and [Link] before continuing.
Run serialver on the Employee class and copy the value into a new serialVersionUID field in
your Employee class. Then, add a new Date field, departmentStartDate, to Employee. Do not
modify toString() to include this field yet. In your constructor(s), default the departmentStartDate
to the hireDate. Make sure your new class works with the serialized objects you created in ;
you should still be able to display the object using ReadDept.
(Solution: [Link].4)
Fix your Employee class using readObject so that if the departmentStartDate was missing in the
serialized object, it would default to hireDate. Verify that this works with ReadDept.
(Solution: [Link].6)
Modify your application that writes the department so that it uses the new Employee class with a
non-default departmentStartDate. After creating the new serialized object, restore the original
version of Employee and verify that you can read the new serialized object with the old class.
(Solution: [Link])
Objectives
Describe the classes and interfaces in the
[Link] package.
The traditional I/O classes in the [Link] package are easy to use, but they are not
very efficient and do not take advantage of services that most operating systems
provide.
¾ Sun introduced the New I/O library (NIO) with JDK version 1.4 to
address those concerns.
¾ The old [Link] library was rewritten to take advantage of some of the
new features.
The new I/O library reads and writes data in blocks, instead of the byte or
character streams of the [Link] package.
¾ Developers read and write data to the buffer, instead of directly to the
stream.
¾ The [Link] package contains a buffer class for each of the primitive
Java types.
Charsets encode and decode data from an I/O device to Java unicode
characters.
In the NIO model, buffers and channels work together to read and write to I/O
devices.
¾ You read and write data to a buffer, instead of directly to the channel.
¾ You then pass the buffer to the channel, which reads or writes it to the
device.
If you want to read from the file, call the read() method on the channel, passing
the buffer:
¾ To get the data from buffer, flip it, then use the get() method:
If you want to write to the file, fill the buffer, flip it, and then call the channel's
write() method.
[Link]
import [Link];
import [Link];
import [Link];
// write to file
[Link]("Hello".getBytes());
[Link]();
[Link](buf);
[Link]("Wrote string \"Hello\" to access_log.");
[Link]();
}
catch (Exception e) {
[Link]();
}
}
}
Try It:
Compile and run [Link] to use buffers and channels to read a file.
Buffer Implementations
The basic class, Buffer, is the superclass for implementations for various types
of data.
¾ It provides methods for getting or putting each of the primitive Java types
except boolean.
There are six other buffer classes, one for each of the primitive Java types
(except boolean).
¾ Each TypeBuffer class provides get() and put() methods that work with
its type.
¾ The CharBuffer class also has a put() method that takes a String
parameter.
Because most I/O devices work with bytes, channels can only read or write a
ByteBuffer.
[Link]
Buffer
[Link]
...
public class DoubleWrite {
public static void main(String[] args) {
try {
String fileName = "constants";
FileOutputStream fout = new FileOutputStream(fileName);
FileChannel fc = [Link]();
ByteBuffer bbuf = [Link](16);
DoubleBuffer dbuf = [Link]();
[Link]();
FileInputStream fin = new FileInputStream(fileName);
fc = [Link]();
[Link](bbuf);
[Link](); Read pi and e from file.
double pi = [Link]();
double e = [Link]();
[Link]();
[Link]("pi = " + pi + " e = " + e);
}
catch (Exception e) {
[Link]();
}
}
}
Buffer Methods
[Link] defines methods that are related to the position, limit, and
capacity of the buffer.
¾ The capacity is defined when the buffer is allocated and is the amount of
data the buffer can hold.
¾ The limit is one more than maximum position that contains valid data or
to which you can write data.
In a read operation, after the channel fills the buffer, the position specifies how
much data has been read from the device into the buffer.
¾ If you want to pull data out of the buffer, you must reset the position to
define where you are getting from and the limit to define how much you
can get.
¾ Use the flip() method to set limit = position and position = 0 so that calls
to get() will retrieve the correct data from the buffer.
In a write operation, after you put() data into the buffer, the position specifies
the last place you put data.
¾ Before you write() to the channel, call flip() to set the limit = position and
position = 0 so that the channel knows what data to write to the device.
Use clear() to reset position = 0 and limit = capacity if you want to do any more
read or write operations on the buffer.
Channel operations, and get and put calls on the buffer, set the position and limit
properly; however, you can retrieve the values or set them with methods in the
Buffer class.
Page 46 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 3 Advanced I/O — New I/O
When you allocate a buffer, an array is allocated to hold the data. The capacity is defined to be the
allocation size of the buffer. The position is initialized to 0 and the limit is initialized to the capacity.
ByteBuffer buf = [Link](8);
0 1 2 3 4 5 6 7 8
ByteBuffer Methods
[Link] defines methods that help get data from and put data in the
buffer.
¾ There are get and put methods for each primitive type except boolean.
¾ There are overloaded get and put methods that allow you to specify an
absolute position within the buffer.
¾ There are also get and put methods to read from or write to a byte array.
Since the file channel's read() method returns the number of bytes
read, it is convenient to allocate a byte array and use this version of
the get method.
You can work directly with the buffer's array by calling the array() method.
You can also supply your own array to a buffer with the static wrap() method.
Most of the Buffer and ByteBuffer methods that do not need to return
something else (eg. get methods), return a reference to the buffer itself.
[Link]().get(bytes);
FileChannel
¾ As with Buffer, the class is abstract and you actually get an instance of a
class defined by a service provider.
¾ Scatter and gather are useful for data that has different sections.
For example, you may have a header section that contains binary
data and a body section that contains UTF-16 encoded text.
[Link]
...
public class Scatter {
public static void main(String[] args) {
try {
String fileName = "mixed";
Try It:
Compile and run [Link] to write an array of float temperatures and an array of String cities to
the mixed file. Use the [Link] program to read the file.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 51
Advanced Java Programming
File Locking
¾ The third argument should be true if you want a shared (read) lock, false
if you want an exclusive (write) lock.
¾ The FileLock also knows about the channel that created it, its start
position, size, and whether it is shared.
The lock() methods block until the requested lock can be obtained.
File locking in Java relies on the operating system implementation, so it will work
differently on different systems.
¾ Some systems do not provide shared locks, if you request one it will be
silently promoted to exclusive.
Locks are implemented within the operating system on a per file, per process
basis.
[Link]
...
public class LockTest {
public static void main(String[] args) {
try {
String fileName = "access_log";
[Link]();
}
catch (Exception e) {
[Link]();
}
}
}
Try It:
Run this program in two separate JVMs to test file locking. Start both programs before pressing <Enter> to
get the lock. The program sleeps three seconds between obtaining and releasing the lock, so you have time
to go to the other instance and press <Enter>.
Note: In the example, we pass Long.MAX_VALUE as the second argument to the shared lock call. This
does not affect the size of the file, and any expansions to the size will still be covered by our lock.
MappedByteBuffer
Until now, we have been transferring data to and from Java buffers, which
encapsulate byte arrays, to the kernel's paging memory so that the OS can write
or read the data to the physical file.
¾ You can avoid the buffer copy by mapping your buffer directly to the
paging memory.
¾ The second and third arguments specify the map's position and size
within the physical file.
¾ When you get() from the buffer, you are retrieving the data as it is in the
file, including any changes made by other processes since you opened the
channel.
¾ force() is like the force() in FileChannel; use this for mapped buffers.
¾ load() causes the entire file to be read into paging memory, and
isLoaded() checks if it is.
Note:
The third argument to the FileChannel map() method is the size of the mapped buffer that you want to
create. If you specify a size larger than the size of the file, the physical file size will increase to the size you
specify. Do not pass Long.MAX_VALUE, as we did with file locking, unless you really want (and have
disks that will hold) seven exabytes (7 x 1018).
¾ These methods do not use buffers; they copy data directly to and from
the kernel's paging area.
At least one of the channels must be a FileChannel, because the methods are
defined in the FileChannel class.
¾ The transferTo() method allows you to send data from this channel to a
WritableByteChannel.
When you call transferTo() or transferFrom(), you specify the start position
and size of the data you want to transfer.
¾ If position + size is greater than the size of the file to which you are
transferring, your transfer will be truncated.
Character Sets
The Charset class provides methods for finding out what charsets are available
in the JVM, and for getting a Charset object for a specific charset.
¾ The static forName(String name) method returns the Charset object for
the specified charset.
¾ Call reset() before you start, and flush() when you are finished.
[Link]
...
public class ReadLog1 {
public static void main(String[] args) {
try {
String fileName = "access_log";
RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
FileChannel channel = [Link]();
Try It:
When you compile and run [Link], you will notice that the output is garbage. That is because the
data in the access_log file is encoded with the US-ASCII charset, but we are reading it as UTF-16.
[Link] decodes and displays the information correctly.
[Link]
...
public class ReadLog2 {
public static void main(String[] args) {
try {
...
ByteBuffer bbuf = [Link](1024);
int count = [Link](bbuf);
[Link]();
Charset ascii = [Link]("US-ASCII");
CharBuffer cbuf = [Link](bbuf);
char[] chars = new char[[Link]()];
[Link](chars);
for (char c : chars) {
[Link](c);
}
...
}
}
Labs
Write a program that reads [Link] and writes it to a filed called JavaSource.
(Solution: [Link])
Write a program that uses a Charset, ByteBuffer, and FileChannel to write the names of
each of the installed charsets to a file. Use ByteBuffer's put() method that takes a byte array
for a parameter. Get the byte array from String's getBytes() method.
Hint: [Link]() throws a BufferOverflowException if the buffer does not have room
for what you are trying to put on it. You can avoid that by checking the position and limit before
you put.
(Solution: [Link])
Change your program from to use a CharBuffer. What is the difference in the output?
(Solutions: [Link], [Link])
Chapter 4 - Reflection
Objectives
Create a JavaBean.
Introduction to Reflection
The Java Reflection API allows programs to find out everything about an object
at runtime.
¾ The Reflection API gives developers a window into what the JVM knows
about an object.
The Reflection API is defined by the [Link] class, and the classes and
interfaces of the [Link] package.
¾ With reflection, you can identify the type of an object, which class it
extends, which interfaces it implements, and which modifiers, such as
public or final, apply to the class.
¾ You can query for the constructors, methods and fields of a class, and
their modifiers.
¾ You can instantiate a class without knowing its name at build time.
Unlike pointers in C++, you can not use reflection to bypass the
visibility modifiers.
¾ You can use a Method object like a type-safe function pointer, but it may
be better to design your calls to work with interfaces.
[Link]
...
public class Person {
private String name;
private String title;
private Date birthDate;
private int id;
The Member interface also defines constants used when calling SecurityManager's
checkMemberAccess().
[Link] identifies the set of members declared within a class or interface, not including
inherited members.
[Link] identifies the set of public members of a class or interface including inherited members.
Constructors
[Link]
import [Link];
import [Link];
try {
pCreate = [Link](parmTypes);
}
catch (NoSuchMethodException nsme) {
[Link](nsme);
[Link](1);
}
try {
Person p = (Person) [Link](parms);
[Link](p);
}
catch (InstantiationException ie) {
[Link]("Instantiation Exception " + ie);
}
catch (InvocationTargetException ie) {
[Link]("Invocation Target Exception " + ie);
}
catch (IllegalAccessException ie) {
[Link]("Illegal Access Exception " + ie);
}
}
}
Try It:
Compile and run [Link] to create a Person object, using Constructor's newInstance()
method.
Fields
To find the public fields of a class, invoke the getFields() method on the Class
object.
Query or modify the value of the field with the set() and get() methods.
The getModifiers() method returns an int, which defines the modifiers for a
member.
¾ The Modifier class defines several static final fields and methods for
decoding the modifier value.
The ReadFields application uses reflection to display the names and values of its public fields.
[Link]
import [Link];
An IllegalAccessException will be thrown from the get() method if the underlying field is
inaccessible.
Try It:
Compile this program to display the public fields of an object. Modify the code to call
getDeclaredFields() instead of getFields() to see all of the fields declared in the object.
Methods
To find the public methods of a class, invoke the getMethods() method on the
Class object.
¾ If there are no parameters, you can substitute null for the empty array.
Invoke the method by providing an object and parameter array to the invoke()
method:
[Link]
import [Link];
import [Link];
This example creates a Person2 object using a Constructor. The corresponding Person2 constructor has
been edited to throw a ParseException.
[Link]
...
public class Person2 {
...
public Person2(String nm, String ti, String bDate, int i)
throws ParseException {
...
}
}
[Link]
...
public class CreatePerson {
public static void main(String[] args) {
Class clazz = [Link];
Class[] parmTypes = {[Link], [Link],
[Link], [Link]};
try {
Constructor constructor = [Link](parmTypes);
Try It:
Compile and run [Link] to test exception handling with reflection.
JavaBeans
If you follow the rules for JavaBeans, you can load your JavaBean into your
IDE's toolbox, drag and drop it onto your window, set its properties, and even
handle events.
¾ The IDE performs all of those tasks by quering the bean class with
reflection.
Page 78 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 4 Reflection
The Person class from earlier in the chapter almost fulfills all of the requirements of a JavaBean
class. It already implements Serializable, but we need to add a no-argument constructor. We should also
provide set methods to match the get methods.
[Link]
...
public class PersonBean implements Serializable {
private String name;
private String title;
private Date birthDate;
private int id;
public PersonBean() {
name = "";
title = "";
birthDate = new Date();
}
...
public String getName() {
return name;
}
public void setName(String nm) {
name = nm;
}
public String getTitle() {
return title;
}
public void setTitle(String t) {
title = t;
}
public int getId() {
return id;
}
public void setId(int i) {
id = i;
}
public String getBirthDate() {
SimpleDateFormat df = new SimpleDateFormat("M/d/yyyy");
return [Link](birthDate);
}
public void setBirthDate(String shortDate)
throws ParseException {
SimpleDateFormat df = new SimpleDateFormat("M/d/yyyy");
birthDate = [Link](shortDate);
}
...
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 79
Advanced Java Programming
Dynamic Programming
While the loading and execution of code in the JVM is dynamic, the actual
programs themselves are static.
¾ IDE views that present your code as a tree structure made up of fields,
constructors and methods are most likely using reflection to generate the
tree.
¾ Debuggers can use reflection to show you the current state of fields in a
running program.
Enterprise Java Beans (EJB) use reflection in a similar way to create instances
and identify properties.
JavaServer Faces (JSF) use reflection to dynamically populate beans with data
that has been declared in an XML file.
Page 80 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 4 Reflection
The Java Community Process (JCP) has identified a need for a dynamic scripting language, where you
can type commands into an interpreter and have them evaluated immediately. You can even define new
types and methods on the fly. Java Specification Request (JSR) 223 defines an API for scripting languages,
and will be included in a future version of Java.
Two other JSRs, 241 and 274, define scripting languages which may be included with a future release
of Java. Both scripting languages use Java-like syntax and provide access to the full power of Java. JSR
241 is Groovy, an agile language, suitable for rapid prototyping and similar in usage to Python and Smalltalk.
Groovy can replace Java in small-to-medium size applications. For more information, see
[Link]
JSR 274 is the BeanShell language, also intended for rapid prototyping and to work with existing Java
applications. For more information, see [Link]
Labs
The chapter directory contains a GUI program called Reflect that is a reflection test bed. All of the
GUI part has been created ([Link], [Link]), however the GUI calls
methods in the Reflect class to find information about classes and objects. Currently the Reflect
class contains stubs for the methods, but you need to fill in those stubs. In the Reflect class, fill in
the code for the getCtors() method to get a Vector of Constructor objects for the given class:
You can test your program at any time by compiling it and running java Reflect.
(Solution: [Link])
Write the code for the instantiate() method. Because we want to be able to create objects with
overloaded constructors, use the newInstance() method in Constructor, not the one in Class.
(Solution: [Link])
Write the code for the getMethods() method, which returns a Vector of Method objects:
(Solution: [Link])
Write the code for the invoke() method to invoke the selected method on the selected object. The
return value should be the return from the invoked method.
(Solution: [Link])
Objectives
The JDBC driver will scan the SQL string for escape clauses.
{keyword parameters}
[Link]("UPDATE employee" +
"SET hire_date = {d '1998-01-05'}");
¾ The driver can then turn the generic JDBC escape sequence into the
appropriate vendor-specific SQL syntax.
¾ Or the driver may itself implement the feature indicated by the escape
sequence, even if the DBMS doesn't.
{keyword parameters}
Keyword Parameter(s)
After an execute(), your Statement object has either a ResultSet, an update count, or neither. In addition,
after you are finished with that result (either the ResultSet or the update count), there may be another result
to retrieve and process. If you have no idea what to expect, you can still process all of your results:
[Link]
...
public class ExecuteExample {
public static void main(String args[]) {
...
Statement stmt = [Link]();
boolean haveResultSet = [Link](sqlstring);
if (haveResultSet) {
ResultSet rs = [Link]();
ResultSetMetaData rsmd = [Link]();
int cols = [Link]();
for (int col = 1; col <= cols; col++)
[Link]([Link](col) + "\t");
[Link]("");
while ([Link]()) {
for (int col = 1; col <= cols; col++) {
[Link]([Link](col) + "\t");
}
[Link]("");
}
[Link]();
}
else { // No result set.
int uc = [Link]();
[Link](uc + " row(s) updated.");
}
[Link]();
[Link]();
...
}
}
Try It:
Compile and run [Link]. Enter an INSERT, UPDATE, or DELETE SQL statements
when prompted.
Batch Updates
¾ You can delete the commands from a batch with the clearBatch()
method.
Call executeBatch() to execute the commands that you added to the batch.
¾ The order of the array corresponds to the order in which the commands
were added to the batch.
It is up to the driver whether it supports batches, and if so, how they are
handled.
[Link]
...
public class Batch {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
[Link]("[Link]");
String url = "jdbc:derby://localhost:1527/j2se";
conn = [Link](url);
[Link](false);
stmt = [Link]();
[Link]("UPDATE department SET manager_id = 7223 " +
" WHERE department_code = 'AC'");
[Link]("UPDATE department SET manager_id = 6881 " +
" WHERE department_code = 'AD'");
[Link]("UPDATE department SET manager_id = 8339 " +
" WHERE department_code = 'CS'");
[Link]("UPDATE department SET manager_id = 8053 " +
" WHERE department_code = 'HR'");
int[] uc = [Link]();
[Link]();
}
catch (BatchUpdateException bue) {
try {
[Link]();
}
catch (SQLException e) {
[Link](e);
}
[Link](bue);
}
...
}
}
Try It:
You can compile and run this program to update the department managers.
While iterating through a ResultSet, you can update or delete the current result
set row from the database if:
[Link](int type)
[Link](int type,
int concurrency)
¾ The SQL statement meets the requirements for updatable result sets:
A general rule is that the query should include the primary key, and
reference only one table.
[Link]("manager_id", newMgrId);
[Link]
...
public class RowIdExample {
public static void main(String args[]) {
...
Statement empStatement = [Link]();
Statement deptStatment = [Link](
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet empResultSet;
ResultSet deptResultSet = [Link](
"SELECT department_code, department_name, manager_id " +
"FROM department" );
Loop through
departments.
while ([Link]()) {
String currentDeptCode =
[Link]("department_code");
empResultSet = [Link](
"SELECT id, department_code " +
"FROM employee " +
"WHERE title = 'Department Manager'");
Loop through
while ([Link]()) { department managers.
String employeeDeptCode =
[Link]("department_code");
if ([Link](currentDeptCode)) {
// determine if current department has a manager_id
// don't update a row that already has a manager_id
[Link]("manager_id");
if (![Link]())
continue;
[Link]("Updating dept " + currentDeptCode);
int mgrId = [Link]("id");
[Link](", setting manager_id to " + mgrId);
[Link]("manager_id", mgrId);
[Link]();
}
}
[Link]();
...
}
}
Try It:
Compile and run [Link] to update manager fields.
Large Objects
Most DBMSs support some form of LOB (Large OBject) datatypes, like LONG,
LONG RAW, BYTE, BINARY, LONG BINARY, IMAGE, etc.
¾ You may also know these as BLOBs (Binary Large OBjects) or CLOBs
(Character Large OBjects).
JDBC 2.0 provides datatypes for these values; see your driver's documentation
for DBMS type equivalencies.
¾ BLOB, CLOB
¾ Call length() to find out how many bytes or characters are in the LOB.
¾ If you are copying from another data element, you can use setBlob() or
setClob().
Blob and Clob are available since JDBC 2.0. If you have a JDBC 1.1 driver, you need to use getBytes(),
getString(), or a getxxxStream() method.
[Link]
...
class ImageFromFile {
public static void main(String args[]) {
try {
[Link]("[Link]");
String url = "jdbc:derby://localhost:1527/j2se";
Connection conn = [Link](url);
[Link](1, image);
[Link](2, idArray[i]);
int uc = [Link]();
[Link]();
}
[Link]();
[Link]();
}
catch (Exception e) {
[Link]();
}
}
}
Try It:
[Link] will populate the Employee table with three images.
Savepoint s = [Link]();
Savepoint sp = [Link]("SaveA");
[Link](sp);
You can call releaseSavepoint() to remove the Savepoint from the current
transaction.
[Link](sp);
JDBCSavepoint creates two update statements, executes the first and then creates a savepoint before
executing the second. Before the commit is performed, a rollback to the savepoint is performed, eliminating
the second update from the transaction.
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link]; Be sure to set
AutoCommit to false in
public class JDBCSavepoint { order to use Savepoints.
public static void main(String args[]) {
try {
[Link]("[Link]");
String url = "jdbc:derby://localhost:1527/j2se";
Connection conn = [Link](url);
[Link](false);
String s1 = "UPDATE employee SET salary = salary * 1.1253";
String s2 = "UPDATE employee SET title = 'CEO' WHERE id = 9683";
Statement stmt = [Link]();
[Link](s1);
Savepoint sp = [Link]("SVP1");
[Link](s2);
[Link](sp);
[Link]();
[Link]();
[Link]();
}
catch (ClassNotFoundException cnfe) {
[Link](cnfe);
}
catch (SQLException sqle) {
[Link](sqle);
}
}
}
Try It:
Compile and run [Link]. To view the changes to the database, ask your instructor how to
list the Employee table.
RowSets
[Link]
import [Link];
import [Link];
while ([Link]()) {
[Link]([Link]("id") + " " +
[Link]("lastname") + " " + [Link]("salary")
+ " " + [Link]("title"));
}
}
catch (Exception e){
[Link]();
}
}
}
Try It:
Compile and run [Link]. This should list all of the employees currently in the Employee
table.
CachedRowSet
[Link](url);
[Link]("SELECT id, lastname FROM employee");
[Link]();
CachedRowSet inherits from ResultSet, allowing you to use the set and update
methods from ResultSet to retrieve and modify the RowSet columns.
¾ Once a column has been updated, call updateRow() to make the change
to the row.
The CachedRowSetImpl comes with JDK, Standard Edition and implements the CachedRowSet
interface. It is part of the [Link] package. In general, it is not a good idea to rely on this package
because the sun.* packages are not part of the supported, public interface. A Java program that directly
calls into a sun.* packages is not guaranteed to work on every Java platform. Such a program is not even
guaranteed to work with future versions of the same platform.
[Link]
import [Link].*;
import [Link];
import [Link];
DataSources
¾ The DataSource is given a name and is deployed using the Java Naming
and Directory Interface (JNDI).
¾ All the properties that describe the data source are kept separate from, and
can be changed independently of, an application.
Deployment Application
Client Application
1. lookup(JNDIname)
Naming Service
Connection Pool
Client
Application Database
Connection
2. getConnection()
DataSource Database
Database Connection DBMS
Connection Reference
Database
Connection
Labs
Write a program that will find all of the Employee records that do not contain null in the mugshot
field. Extract each mugshot into a file using the lastname of the Employee as the filename.
(Solution: [Link])
Write a program to list the lastname, hire_date, and salary of all employees who started
before January 1, 1995. Put all of the records into RowSet and display the information from
the RowSet.
(Solution: [Link])
Modify the previous program so all long-term employees make at least $50,000 a year. Be sure to
populate your changes back to the database.
(Solution: [Link])
Modify the program one more time to insert yourself into the RowSet. The steps for insertion will
be the same as ResultSet's steps, since RowSet inherits from ResultSet.
(Solution: [Link])
The following SQL statement will find all employees that have mugshots:
Objectives
A client typically contains the user interface, but uses a server to interact with a
database or some other resource.
¾ The client may be unable to interact directly with the resource, or it may
just be more efficient to have a single server program accessing the
resource.
A server can even act as a client, requesting services from another server.
In this chapter you will create a client-side socket communications class that communicates through sockets
with a server program (which you will also create). Provided is a GUI-based client program that will use
your socket communications class. The name of the provided client program is ClientGUI. The class you
create will be called SocketHandler, with one static method named echo().
A client also needs to know which process on the host is the actual server
program.
¾ Process IDs change every time a program starts, so each host has a set of
numbers called ports which identify servers.
A client must know the host, port, and protocol of the server in order to find
and communicate with it properly.
Try It:
Compile and run [Link]. When the send button is pressed, ClientGUI passes the data from the
sendArea to SocketHandler, which currently just returns the data to ClientGUI, who displays it in the
receiveArea. Look at the source code for both [Link] and [Link]. We will
develop SocketHandler to send the data through sockets to a server, which will simply echo the data
back, and then SocketHandler will return the echoed data to ClientGUI. We will not modify
[Link].
[Link]
...
public class ClientGUI extends JFrame {
...
JButton send = new JButton("Send");
[Link](new ActionListener() {
public void actionPerformed(ActionEvent e) {
String host = [Link]();
int port = [Link]([Link]());
String textToSend = [Link]();
[Link]
public class SocketHandler {
public static String echo(String host, int port, String input) {
return input;
}
}
The Socket constructor locates the host on the network and connects to the
server listening on the given port.
¾ Additional methods and classes exist for using UDP and other protocols.
Hands On:
[Link]
import [Link].*;
import [Link].*;
}
catch (UnknownHostException e) {
[Link]("UnknownHost" + [Link]());
[Link](1);
}
catch (IOException e) {
[Link]([Link]());
[Link](2);
}
return receive;
}
}
Once a client has constructed a Socket object, all communication between the
client and server is done using the Java input and output classes.
The getOutputStream() method returns the OutputStream object that you use
to send data to the server.
The getInputStream() method returns the InputStream object that you use to
read data from the server.
¾ You can convert these streams to any I/O class that you would like.
Any type of data can be sent across these streams: String objects, Java
primitives, or even Serializable objects.
¾ You need to pick an I/O class that is appropriate for your data.
Calling close() on the Socket will send an end-of-file to the server; do not forget
this!
Hands On:
Add the I/O code to SocketHandler. The query string from the ClientGUI sendArea may have
embedded newline characters, and each line will come back from the server individually, so we will send a
"\u0004" and when it comes back we will know that everything sent has been echoed.
[Link]
...
Socket s = new Socket(host, port);
[Link](input);
We'll use this to
[Link]("\u0004"); end messages.
Try It:
Compile your client. If you are on a host or network with INET services (UNIX or Linux hosts will have
these available) you can run your client against port 7, the standard echo server. Otherwise you will have to
wait until you have built your server . . .
Servers
Server programs are typically daemons: they stay running in the background all
the time.
Your server will loop infinitely, waiting for clients to try to connect it.
When requests start coming in, you have two ways to handle the requests:
iteratively or concurrently.
You would use an iterative server when the service provided is quick, or the
service is based on scarce resources.
¾ An iterative server may be able to handle the client request quicker than it
could handle the separate thread or process.
¾ If more requests come in while the server is busy serving the previous
one, they are queued by the underlying network software layer.
Hands On:
Let's begin building the server side. Create a class named EchoServer with a main() method. We will leave
the try and finally blocks empty for now.
[Link]
import [Link].*;
import [Link].*;
}
catch (IOException e) {
[Link]([Link]());
}
finally {
}
}
}
The accept() method is a blocking call that returns a Socket object when a
client connects with a Socket.
Socket s = [Link]();
¾ The Socket object returned by accept() has the client address, port, and
protocol.
Retrieve the input and output stream objects to send and receive data.
2b. Retrieve I/O stream objects from the Socket and communicate
with the client.
Hands On:
Construct a ServerSocket on your own port. Ask the instructor for the appropriate port number. Create a
Socket object when a client connects. Read each line from the client and echo it back through the socket.
[Link]
...
public class EchoServer {
public static void main(String[] args) {
Socket clientSocket = null;
BufferedReader sockin = null;
PrintWriter sockout = null;
try {
ServerSocket listenSocket = new ServerSocket(7777);
while(true) {
clientSocket = [Link]();
String linein;
// Read from socket until client closes its end
while ((linein = [Link]()) != null) {
[Link](linein);
[Link]("Server DEBUG: " + linein);
}
[Link]("Server DEBUG: Connection closed");
[Link]();
[Link]();
[Link]();
}
}
catch (IOException e) {
[Link]([Link]());
}
finally {
try {
if (clientSocket != null)
[Link]();
}
catch (IOException e) {}
}
}
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 119
Advanced Java Programming
Concurrent Servers
Concurrent servers are more complex to design, build, and maintain than
iterative servers.
Concurrent servers can be faster than iterative servers if each client will take a
long time to process, and if there are enough threads in the operating system for
each client.
1. Provide the service to the client via the Socket returned from the
accept().
[Link]
...
public class ConcurrentServer implements Runnable {
private Socket client;
private Thread theThread;
private static int count;
private static ThreadGroup threadGroup; Create a new thread.
public ConcurrentServer(Socket s) {
client = s;
theThread = new Thread(threadGroup, this, "Socket" + count);
[Link]("Client connected to server: " + count +
", Current active threads: " + [Link]());
count++;
[Link]();
}
public void run(){ Start the thread.
BufferedReader sockin = null;
PrintWriter sockout = null;
Get the input stream
try {
from the client socket.
sockin = new BufferedReader(
new InputStreamReader([Link]()) );
sockout = new PrintWriter( [Link](), true);
while(true) {
// wait for the connection
Socket s = [Link]();
// create a threaded object to handle the client
new ConcurrentServer(s);
}
}
...
}
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 121
Advanced Java Programming
Typical protocols for a URL include HTTP, FTP, file, and mailto.
A URL can be constructed in much the same way you would type a URL into a
web browser:
¾ The URL protocol, host, port, and file can also be specified individually:
Data can be retrieved from the URL object using one of three methods:
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Note:
This program may fail to connect if you are inside a firewall without a configured proxy server. To specify
proxy information, add the following two lines of code:
[Link]().put("proxyHost", "Host_IP");
[Link]().put("proxyPort", "Port_Number");
Replace Host_IP and Port_Number with the appropriate values for your proxy server.
¾ For instance, if the data is very long, you may want to create an input
stream to read it, or if it is an image, you may not want to retrieve it into a
String.
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
conn = [Link]();
Labs
Create an application that takes the URL for an image on the command line and writes a local copy
of the image. The program should verify that the URL actually points to an image using
[Link](). You can test your program with an arbitrary image from the
internet using HTTP or FTP protocol, or with a file on the local machine using file protocol.
(Hint: The content-type header for most image types starts with image/.)
(Solution: [Link])
Create a server that waits for a connection and sends an arbitrary text file to any client that
connects to it. (Hint: The server writes to the socket, but does not read from it.)
(Solutions: [Link], [Link])
Write a client to access the server in . (Hint: The client reads from the socket, but does not write
to it.)
(Solution: [Link])
Modify the server from so that it stores usage statistics, such as the total number of connections,
in an object. Have the server listen on a separate administrative port and send the statistics object to
the client. (Hint: The server must listen concurrently to both ServerSocket objects.)
(Solutions: [Link], [Link])
Write an administrative client to read the statistics object from the server in .
(Solution: [Link])
Objectives
Distributed Applications
In a distributed application, part of the work is done on one host and part is
done on another.
¾ RMI takes care of the socket work and allows you to invoke methods of
an object that is running in another Virtual Machine, on the same or a
different host.
¾ Servers have objects that are declared remote; these objects can be
accessed from clients.
¾ A particular part of the system may be both a client and a server, serving
some remote objects and accessing those from other servers.
There are three ways that a client can access a remote object:
2. By having the object returned to the client from a different remote object.
3. By having the object sent to it as a parameter to one of its own remote methods.
Only option 1 allows a client that has no remote objects the opportunity to get one. The other two options
require that a remote object is already being referenced by either the client or the server.
Stubs
¾ The server's code is compiled with rmic, which creates the stub .class
files.
The stub performs marshalling, turning the client's arguments into a marshal
stream (datastream) to be sent over the network.
On the server side, the stub unmarshals the arguments and calls the method, then
marshals the return value or exception.
The client stub then unmarshals the return value and returns it to the calling
applet or application.
The client code simply locates the remote object and calls its methods as though
it were a local object.
¾ Remote objects may have other methods which are not available to the
client.
Client's Java VM
Stub marshalled
arguments
Application datastream
methodA()
methodB()
return value
Server's Java VM
Remote Stub marshalled
return value datastream
methodA() methodA()
methodB() methodB()
arguments
methodC()
...
¾ The methods in the remote interface are the methods available to clients.
3. Compile the .java files with javac to create the .class files.
javac [Link]
javac [Link]
4. Compile the remote class with rmic to create the stub .class files.
rmic MyRemoteClass
5. Distribute the .class files for the interface and the stub (and any inner classes
they use) to the client.
One of the enhancements to RMI in Java 5 is automatic stub generation. If the VM cannot locate the
stub class in its classpath when you export an object, it will automatically generate a stub class. You
must still use rmic, however, if you are supporting clients that run on a pre-Java 5 VM.
An RMI Client
RMI makes working with remote objects very similar to working with local
objects.
//hostname:1099/objectname
Once you have a reference to the object, call its methods as you would with a
local object.
In order to compile, the client code must have access to the remote interface.
client/[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public ShowTimeClient() {
Time t;
try {
t = (Time) [Link]("///Time");
[Link]("The time is: " + [Link]());
}
catch (NotBoundException e) {
[Link](e);
}
catch (MalformedURLException e) {
[Link](e);
}
catch (UnknownHostException e) {
[Link](e);
}
catch (RemoteException e) {
[Link](e);
}
}
}
An RMI Server
The remote code consists of two parts, which may be in one class or separated
into two:
The server portion creates an instance of the remote class and registers it with
the RMI Registry through [Link]() or [Link]().
The RMI Registry is a process that keeps track of the objects on a particular host
that are available remotely.
¾ Several servers can register objects with the RMI Registry, or a single
server can register multiple objects.
¾ Every remote method call will run its own separate thread.
server/[Link]
import [Link];
import [Link];
server/[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Try It:
From within the server directory, compile the remote interface and server class by running
javac Time*.java. Then generate the stub class by running rmic TimeServer.
protocol://host:port/name
¾ The protocol defaults to rmi, host defaults to the local machine, and port
defaults to 1099.
rebind() does the same thing, but replaces an existing object with
the same name.
Other classes and interfaces are rarely used by the programmer to implement RMI. Some are used to
create customized registries and servers.
Class Distribution
An RMI client needs access to the server's interface and stub class files.
¾ Access to the interface is required at build time so the compiler can verify
the method calls.
Often, the interface and stub class files are distributed along with the client
application.
It is possible to dynamically download the stub class file (and any other
necessary class files) at runtime.
¾ To use this feature, you must install a security manager in the client and
provide an appropriate policy.
The RMI Registry must also have access to the stub class file.
¾ You can also specify the codebase for the class file when you start your
server:
If the registry does not find the stub class file in the classpath, it
will load it from this location.
Try It:
2. Copy the interface class file from the server: copy ..\server\[Link]
4. Copy the stub class file from the server: copy ..\server\TimeServer_Stub.class
RMI Utilities
rmic generates the stub class file for classes implementing the [Link]
interface.
rmiregistry [port]
7. Register the remote class by running the server code with java.
Try It:
1. Start the RMI Registry:
a. Open a new command prompt window and cd into the root directory.
b. Run: rmiregistry
Note: Your codebase URL may be different, depending on where your class files are installed.
3. Run the client program in your original command prompt window: java ShowTimeClient
The stub and interface class files must be available to the client.
¾ Objects that implement the Serializable interface are serialized and passed
by value.
The class file must be available to both the client and server.
If you return a remote object from a remote method, that object will be available
to the client remotely.
¾ RMI developers can use the factory design pattern to give each client a
unique server object.
¾ This is useful for situations where there are many related transactions or
where the transaction may take some time.
Page 146 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 7 Remote Method Invocation
server/[Link]
package server;
import [Link];
import [Link];
import [Link];
import [Link];
server/[Link]
package server;
import [Link];
import [Link];
Labs
In this chapter directory there are two remote interfaces, Store and Attendant. There is also a
server implementation for each interface. The StoreServer binds a server object to the "///Store"
URL. Write a client program that locates the store object, gets an attendant, and places an order.
Release the attendant when you have finished placing your order. Compile and test your application.
(Solution: [Link])
Create a Serializable Order class that contains information about an order. The order can include
several itemcode/quantity pairs. Change the submitOrder() method in the Attendant interface so
that it accepts an Order object as a parameter. Make the appropriate changes in the
AttendantServer. Remember to change the Store interface and StoreServer to use the new
Attendant. Compile and test your application.
(Solutions: [Link], [Link], [Link], [Link],
[Link], [Link])
The store has an inventory, which is an array of Item objects. Add methods to the Attendant that
allow a customer to retrieve a list of the item codes as a String[], and an individual Item by
itemcode. Since Item includes a quantityOnHand field, you should make it remote so that your
customer knows about any changes. Change your customer to use the new methods. Compile and
test your application.
(Solutions: [Link], [Link], [Link], [Link],
[Link], [Link])
[Link]
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
Objectives
Make an RMI client act as a server through
client callbacks.
Client Callbacks
¾ A client callback lets the remote server call methods on the client.
¾ The roles of "client" and "server" are now temporarily reversed — the
remote server now invokes a remote method on the client.
¾ Create an interface which extends Remote and declares the client callback
method.
¾ In the client, create an object that implements the remote interface, and
register the object by passing it to the remote method on the server.
¾ When the server event occurs, the server calls the client callback method
for each registered client.
¾ A separate thread in the client will execute the client callback method.
[Link]
import [Link];
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
At runtime, RMI clients need class files for interfaces, stub classes, classes
returned by remote methods, and any classes that are used by those classes.
A codebase is a location from which a Java program can load class files.
¾ If the client cannot find the class file in its classpath, it will attempt to load
the class file from the codebase.
In order to use dynamic loading, you must install a security manager in your
client.
¾ You need a policy file for your client that grants permissions for
connecting to the RMI Registry and remote objects.
If a client passes objects to a server, it may also need the codebase property.
bank/[Link]
package bank;
import [Link];
bank/[Link]
grant {
permission [Link] "*:1024-", "connect,resolve";
};
This policy file allows the client to connect to services on any non-priviledged port. This includes the
rmiregistry (port 1098), as well as any Remote objects. If the server's codebase specifies an HTTP
service, we need to add an entry to allow connection to port 80.
Now that we have installed a security manager, any task the client performs that has security
restrictions must be permitted in the policy file, even if it has nothing to do with RMI.
Activation
¾ Activatable remote objects are only created when a client needs to call a
method, saving resources in systems with many remote objects.
¾ Sun provides rmid as the standard activator that comes with the JRE.
Since activatable remote objects need not be instantiated until accessed, they do
not run in a virtual machine until activated.
¾ The activator monitors and manages the VM for each activation group,
starting it when needed.
Activation is invisible to the client, which uses activatable objects in exactly the
same way it uses standard remote objects.
Activatable Objects
Create activatable objects in the same way you create other remote objects with
the following exceptions:
¾ The rest of the steps and requirements are the same as for standard
remote objects.
For example, you could provide a primary key for use in a JDBC
query.
bank/[Link]
package bank;
The interface has nothing
import [Link]; in it specific to activation.
import [Link];
bank/[Link]
... This is the constructor
public class BankAccountImpl extends Activatable used by the activator
implements BankAccount { to activate the object.
private File accountFile = null;
// scan a tab delimited file for the balance for the given id
public double getBalance(String id) throws RemoteException {
double result = 0;
Scanner scanner = null;
...
return result;
}
}
¾ This method returns a Remote stub which you could use to pass a
reference to the activatable object to clients.
¾ classname is the name of the class that implements the remote interface.
¾ Use the return value when you register your activatable objects.
Since the activator starts the VM for the activation group, you must use an
ActivatonGroupDesc to specify properties and arguments to the VM.
bank/[Link]
...
public class BankSetup {
public static void main(String[] args) throws Exception {
...
String policy = args[0]; Provide a MarshalledObject
Properties props = new Properties(); containing the file from which the
[Link]("[Link]", policy); BankAccount reads balances.
ActivationGroupID groupId =
[Link]().registerGroup(group);
When you run this code (we will give you directions at the end of the activation discussion) you should
notice that, unlike standard RMI servers, it returns right away.
¾ You must provide a policy file if the default policy is too restrictive.
Since the default policy does not usually allow your activatable
objects to accept connections you will most likely need a policy
file.
There are a few differences in how you use dynamic class loading with
activatable classes:
¾ Since you do not have direct control of the VM in which your object runs,
you pass the codebase to the ActivationDesc constructor.
¾ Your client's policy file must allow connections to the activator's port (the
default for rmid is 1098).
bank/[Link]
grant {
permission [Link] "*:1024-", "accept,resolve";
permission [Link] "[Link]", "read";
};
This policy file will allow remote objects in the activation group to accept connections on any non-
privileged port. It will also allow the remote objects to read the [Link] file in the current
directory.
This policy file should be assigned to the [Link] property in the Properties passed to
the ActivationGroupDesc constructor.
bank/[Link]
grant {
permission [Link]
"-[Link]=*";
};
This policy file will allow rmid to assign a value to the [Link] property when starting a
VM for an activation group.
bank/[Link]
grant {
permission [Link] "*:1024-", "connect,resolve";
};
This policy file will allow the BankClient to connect to non-privileged ports so that it can connect to
remote objects, as well as rmid on port 1098 and rmiregistry on port 1099.
Try It:
The steps for compiling and running the bank example are listed in the file [Link] in the
online files for this chapter.
The Java Naming and Directory Interface (JNDI) is an API that allows you to
access various directory services in a standardized way.
Sun's virtual machine includes a JNDI service provider for accessing the RMI
Registry.
[Link]("stock", server);
Stock s = (Stock)[Link]("stock");
JNDI also allows you to configure the InitialContext through system properties or a [Link] file.
java -[Link]=[Link]
-[Link]=rmi:/// StockClient
If you want to allow the use of properties, you can use the no-argument constructor for InitialContext.
Several JNDI service providers allow you to bind remote objects in the service. Examples included with
Sun's virtual machine are the COSNaming service provider for CORBA naming services, and the LDAP
service provider which allows you to specify remote object references as attributes of an entry in an LDAP
directory.
If you use JNDI instead of accessing the registry directly via the RMI classes, then it may be possible to
migrate to a different kind of naming service without modifying any code. Using the standard JNDI
properties, and perhaps a bit of extra code, you can easily allow the end user to determine which naming or
directory service to use.
RMI-IIOP
RMI-IIOP uses IIOP as the networking protocol for RMI components, allowing
developers to implement CORBA components in Java using RMI.
RMI-IIOP requires some changes to the way you develop RMI components.
¾ You must run rmic with the -iiop option to generate IIOP compliant stubs
and ties.
To access an RMI component from CORBA code, you will need to generate the
IDL interfaces for the RMI component by running rmic with the -idl option.
[Link]
...
public class TimeImpl extends PortableRemoteObject implements Time {
public TimeImpl() throws RemoteException {
}
public String getTime() {
Date d = new Date();
return [Link]();
}
}
[Link]
...
public class TimeServer {
public static void main(String[] args) {
try {
TimeImpl time = new TimeImpl();
Context initialNamingContext = new InitialContext();
[Link]("TimeService", time );
[Link]("Time Server is ready...");
}
catch (Exception e) {
[Link]("Trouble contacting TimeService: " + e);
}
}
}
The InitialContext
[Link] must be configured
... using system properties
public class TimeClient { or a [Link] file.
public static void main(String args[]) {
try {
Context ctx = new InitialContext();
Object objref = [Link]("TimeService");
Time time =
(Time) [Link](objref, [Link]);
[Link]([Link]());
}
catch(Exception e) {
[Link]();
}
}
}
TryIt:
Instructions for building and running the Time example are in [Link].
© 2011 ITCourseware, LLC Rev 5.1.4 Page 167
Advanced Java Programming
Labs
Create a command-line chatroom that uses client callbacks. The chatroom will allow for
multiple clients. The chat interface should have a postChat(String msg, String sender)
method, and the client callback should then print the message whenever other clients send a
message.
(Solutions: [Link], [Link], [Link], [Link])
Create an activatable remote object that returns book titles from ISBN numbers. For this
exercise, just hard code the ISBN numbers in a HashMap. The file [Link] contains some
data you can use to fill in the HashMap.
(Solutions: ISBN_IF.java, ISBN_Impl.java, [Link], [Link], [Link],
[Link])
(Optional) Convert the stock remote server and client from the chapter code to use the IIOP
protocol.
(Solutions: [Link], [Link])
Objectives
Untrusted Code
Ask software developers about security, and they will likely talk about any
number of topics.
This third topic is the only one that has been addressed by Java
since version 1.0.
¾ One of the first types of Java programs, the applet, is downloaded from a
web server and executed in a browser's JVM.
¾ The rest of this chapter discusses how you can define policies by which
your JVM grants permissions to applications.
Page 172 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 9 Managing Security Policies
Whenever you design an application that is intended to dynamically download code, you are in effect
bypassing some of your network's security mechanisms. This code distribution via HTTP can tunnel through
firewalls, which often do not allow binary downloads on non-standard ports. Usually this is not an issue
within an intranet, but if your application is designed to serve clients on the internet, make sure that
administrators on both the client and server sides are on board with your plans.
Try It:
There is an RMI application in this chapter directory that gets the server's time. To run it, first go to the root
directory and run rmiregistry.
Open another command prompt window, cd into the server directory in this chapter directory and
compile [Link]: javac [Link]. Generate the stubs for TimeServer: rmic
TimeServer. Run the server program: java -[Link]=[Link]
server/ TimeServer (your codebase may be different).
Open another command prompt window, cd into the client directory in this chapter directory and copy
[Link] from the server directory. We are going to dynamically download the stub, so do not copy it
into the client directory. Compile [Link]: javac [Link].
When you try to run the client program (java ShowTimeClient) you will get an error message:
Investigate:
When you are tracking down security problems, pay attention to the exception messages; they usually give
you very good information about what needs to be fixed. What do you need to do to ShowTimeClient to
enable the RMI class loader?
Security Managers
Java applications (that is, those run with the java command) do not install a
SecurityManager automatically.
¾ That is why most Java applications have access to all system resources by
default.
[Link](new SecurityManager());
Hands On:
Edit the ShowTimeClient program to install a security manager:
client/[Link]
...
public class ShowTimeClient {
public static void main(String args[]) {
[Link](new SecurityManager());
new ShowTimeClient();
}
...
}
Now, when you compile and run your program again, you will get a different exception:
This is because socket (RMI) connections are a protected resource, and we have not granted any
permission.
Java's "Write Once, Run Anywhere" motto does not mean you have to allow
code from anywhere to run unchecked on your machine.
You can specify security policies for your VM that are applied according to the
code base (that is, the location) of programs you run.
¾ Policies can also be applied based on the signer of a .jar containing the
code.
A few of the more obvious examples of permissions you will want to control
include:
Most of the permissions listed above have multiple targets or actions. The Java documentation contains
excellent information about the possible targets and actions and the associated risks. The API
documentation for each permission class is a good place to look, as well as the docs/guide/security/
[Link] page.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 177
Advanced Java Programming
Policy Entries
Policy files contain entries that grant permissions to code, based on where the
code is loaded from at runtime.
¾ If the URL ends in a slash, /, then it matches all .class files in that
directory.
¾ If it ends in /*, then it matches all .class files and .jar files in that
directory.
¾ If it ends in /-, then it matches all .class files and .jar files in and under
that directory.
grant {
permission [Link]
"[Link]", "connect";
}
A list and description of all the various permissions, when they are checked, and possible values can
be found in the JDK documentation. See docs/guide/security/[Link] for more information on
each permission.
For file permissions, you must specify the file or files over which the permission is granted. The special
token "<<ALL FILES>>" means (surprise) the permission applies to any file on your machine.
You can specify individual files or directories, using either relative or absolute paths (note the double
backslashes necessary for Windows filepaths). For directory paths, the convention is similar to that for
codebase URLs. For a specific file:
Read the contents of all files in the directory /tmp and its subdirectories:
You can even use the values of Java properties in permission targets:
Finally, you can use the special token ${/} instead of a platform-specific file separator:
When specifying socket permissions, you must specify the host and the port for which the permission
is granted. The host can consist of a hostname (wild cards are allowed) or an IP address. The port can
consist of a single port, or a closed or open range of ports. The special token localhost (or an empty
hostname) refers to the local machine.
Policy Files
When the VM starts, it will read two policy files by default: a system policy file
and one for the individual user.
The system policy file is named [Link] and is located under the JRE's
lib/security/ directory.
The user-specific policy file is named .[Link] and is located under a user-
specific directory.
If you create additional security policy files, you will need to specify them on the
command-line when you run Java programs.
¾ This adds [Link] to the list of policy files applied for this execution
of MyApp:
¾ Use == to cause [Link] to be the only policy file used for this
execution of MyApp, ignoring the default policy files:
The System Administrator can list additional default policy files by adding them in the JRE's
lib/security/[Link] file. This file starts out with:
[Link].1=${[Link]}/lib/security/[Link]
[Link].2=${[Link]}/.[Link]
To add more default policy files, the administrator just adds them to the list, giving each a new number:
[Link].3=/usr/local/BizApp/admin/[Link]
[Link].4=${[Link]}/.[Link]
[Link].5=[Link]
Note that if a [Link] number is left out, no [Link] with higher numbers will be used:
[Link].7=/java/will/not/use/this/[Link]
[Link].8=/this/[Link]
Policy files are text files you can maintain with any text editor.
For convenience, though, the JDK provides a GUI program, policytool, for
creating and maintaining entries in policy files.
policytool validates the syntax of your entries, provides a list of the standard
permissions, and can edit any policy file.
Hands On:
Start the policytool. The first time you run it, policytool may complain that it cannot find your personal
policy file (because, of course, you have not created it yet). Note the name of the default policy file the
policytool expected to find (you will use it in a moment), and otherwise ignore the error message for now.
Click Add Policy Entry. Enter [Link] (your codebase location may be different) in the
CodeBase textbox, then click Add Permission. Select SocketPermission from the Permission list. In
the text field next to the Target Name list, enter the host and port: *:*. From the Actions list, choose
connect, then choose resolve. Click OK when you are finished.
Because the server codebase uses a file URL instead of an HTTP URL, we also need to grant a
FilePermission to our client. Click Add Permission again. Select FilePermission from the Permission
list. In the text field next to the Target Name list, enter the stub location: C:\\advj2se\\ch09\\server\\- (your
stub location may be different). From the Actions list, choose read. Click OK when you are finished and
click Done on the Policy Entry window. You have granted permission to access the server and download
the stub.
Now save your policy entry, creating your default policy file. To do this, choose Save As in the File menu.
Use the file dialog to specify the correct path and filename for your default policy file. This is the path the
policytool mentioned in the message dialog when it started up the first time. On Windows, if your account
name is student, the full path should be something like C:\Documents and Settings\student\.[Link].
On UNIX, it should be something like /home/s1/.[Link]. Note the leading '.' in the filename! Close
policytool and try running the ShowTimeClient again. If you specified the paths and permissions correctly,
the client should be able to connect to the TimeServer.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 183
Advanced Java Programming
Securing Applets
In Java 1.0, all applets were locked in a security sandbox and were not allowed
to make "dangerous" method calls.
In Java 1.1, an applet was locked in the sandbox unless it was loaded from a .jar
file that included a recognized digital signature, in which case it was trusted and
granted full access.
Neither the Java 1.0 nor Java 1.1 approach is very effective, so both Netscape
and IE came up with their own, proprietary security mechanism and API.
To use the Java 2 security model, you must use the Java Plug-In.
¾ If the applet is loaded via an <APPLET> tag, the browser might use its
built-in 1.1 VM.
Try It:
The WriteFile program can be run as an applet, loaded from the [Link] file. Compile and load
[Link] into the appletviewer that comes with the JDK:
appletviewer [Link]
When you click the Write button, the applet attempts to write the file specified in the text box. Our security
policy has not granted file write permission, so the applet fails.
Securing Applications
To have policy entries apply to a Java application run with the java command,
you must install a SecurityManager.
[Link](new SecurityManager());
Try It:
The WriteFile program can also be run as an application: java WriteFile. Try running it with a security
manager to see if your policies are applied: java -[Link] WriteFile.
Labs
Edit your policy entry to include a FilePermission with a write action. Try running the WriteFile
applet in appletviewer and see if you can save the file.
(Solution: [Link])
Run the WriteFile applet in appletviewer. Is an exception thrown when you click the Close
button? Why?
(Solution: [Link])
Use the policytool to grant the RuntimePermission target exitVM for this codebase. Be sure to
save your policy before going on.
What happens when you restart appletviewer and click the Close button of the WriteFile applet?
Does your applet have the necessary permission now?
(Solution: [Link])
Add code to the TimeServer program to install a SecurityManager when the program starts.
Now run the server (do not forget to start the rmiregistry). Run the ShowTimeClient. Add the
correct permission to your policy to allow the server to accept connections.
(Solutions: [Link], [Link])
Currently, your policy file grants all the permissions needed by any application that we've worked
with to all applications in this chapter directory. This could open security holes. For example, the
WriteFile applet could accept socket connections. A better approach is to only grant permissions
that are required for a program to do its job.
Create a new policy file that contains the permissions that TimeServer requires, and remove them
from your .[Link] file. Run TimeServer with just the policy file that you created.
(Solution: [Link])
Objectives
Jar Files
A Java Archive (JAR) is a file used for storing and distributing Java programs
and components.
¾ A JAR usually contains Java .class files, supporting files, and sometimes a
manifest describing what is in the JAR.
Create a JAR with the jar utility that comes with the JDK:
¾ You can create or look at a JAR with any zip utility because JARs use zip
compression.
However, only Java's jar utility will add the manifest file for you.
You don't need to extract the JAR to use it — Java can extract what it needs at
runtime.
¾ To use classes from a JAR, include the JAR file (not just the directory
that contains the JAR file) in your classpath.
Hands On:
Compile the [Link] program that's in the working directory for this chapter:
javac [Link]
Look at what is in the JAR file. Notice the jar utility created a META-INF/[Link] file:
appletviewer [Link]
¾ Was anyone else able to intercept and read the data's content?
And, if the sender is someone I trust, just how far do I trust him — specifically,
if he sends me a Java program, will I allow his program to:
Using the JCA, you can write applications that generate message digests and
digital signatures, interact directly with keystores, generate keys, create
certificates, define and check permissions, and more.
Any electronic message can potentially be intercepted and modified, without the recipient's knowledge.
Message Digests
From any digital message of any length, a digital digest can be computed.
¾ There's no way, given the digest, to reproduce any part of the message;
digest algorithms are completely one-way.
If a sender calculates and tells you the digest of a transmitted message, and the
digest you calculate from the message you receive matches it, then you can be
confident the message was not altered during transmission.
¾ However, the sender must somehow communicate the digest value to you.
¾ If someone were to intercept both the message and the correct digest, he
could substitute a falsified message, plus a new digest of the falsified
message.
Try It:
Compile and run the [Link] program, including the string you want
to create a digest for as a command-line argument: java MessageDigestTest "Four
score and seven years ago . . ."
A digest comparison assures recipients that they received the message exactly as it was sent. The sender
could tell the recipient the digest in a separate message, by telephone or by some other means.
If the digest actually accompanies the message, though, someone who wants to tamper with a message
can simply delete the original digest, alter the message, recalculate the new digest using the same algorithm,
and attach it to the altered message:
Digital Signatures
The digest of a digitally signed message is encrypted using the sender's private
key.
¾ The recipient decrypts the digest using the sender's public key.
¾ They don't have access to the sender's private key, which is necessary to
create a signature the public key can decrypt.
¾ Only the private key of the authentic sender works with his public key.
¾ By the way, the sender of a signed message can't claim later that he didn't
send it.
The digest of a digitally signed message is encrypted with the sender's private key. The recipient uses the
sender's previously-published public key to decrypt the digest.
Using keytool
keytool stores your keypairs (you can have more than one) in a keystore.
Each key in a keystore has a unique alias — just a short, convenient name for
that key.
¾ Unless you specify an alias with the -alias option, keytool uses mykey as
the default.
¾ keytool prompts you for certain information about the entity (person or
organization) to whom the key belongs.
Generating a Keypair
Hands On:
Use keytool to generate a keypair for yourself. Use your lastname (or your unique login ID, if you prefer)
as the alias. You will be prompted for a keystore password, so choose something easy to remember (you
must enter this every time you run keytool). Enter appropriate values for the entity information. Note
carefully what happens when you enter yes to confirm your entry — generating a keypair is computationally
expensive, hence there may be a delay. After your key is generated, use keytool to view the contents of
your keystore:
keytool -list
keytool -v -list
Using jarsigner
jarsigner uses your private key to create a digital signature for a JAR's contents.
If any of the JAR's contents are changed, the JAR will fail verification unless the
signature is regenerated.
¾ jarsigner -verify will tell you if any file in a JAR doesn't match the
signature.
¾ The signature file contains digests, not of the JAR's contents, but of the
JAR's manifest entries.
¾ The signature block file contains the signature (digitally signed digest) of
the signature file.
The signature block file also contains a certificate for the signer's
public key.
Hands On:
. . . where keyname is the alias you used when you generated your key. You will have to enter the password
for your keystore. Go ahead and use jarsigner to verify your signed JAR:
Try the -verbose option (keytool uses -v, jarsigner uses -verbose):
Use appletviewer to make sure you can load the classes from the JAR:
appletviewer [Link]
Now, make a modification to [Link], recompile, and update the JAR. For the modification, simply
change the value of the filename variable from [Link] to [Link]. Be sure to use jar's u (update)
option, not c:
javac [Link]
jar uvf [Link] WriteFile*.class
Use jarsigner -verify again to verify the modified JAR. Try loading the JAR's classes in appletviewer.
Re-sign [Link]:
Certificates
Signed code is trustworthy only if the public key the recipient uses to verify it
actually belongs to the entity they think it belongs to.
¾ If you trust the issuer of the certificate, then you can trust the public key it
contains.
When you sign a JAR, the JAR will include a certificate for your public key.
¾ If the recipient trusts the issuer of the certificate, then they can trust that
the JAR was, in fact, signed by you.
If recipients of your data trust you, they import your certificate into their
keystores.
The base [Link] class does not give you a lot of information about a certificate,
because it is generic, and does not represent any specific format. By default, keytool creates an X509
certificate, which is an ANSI standard and the dominant format.
[Link]
...
public class ShowCert {
public static void main(String[] args) {
try {
String keyStoreLocation = [Link]("[Link]");
String keyStoreFile = ".keystore";
String password = "Hello, I must be going";
String alias = "firefly";
Try It:
Change the keyStoreFile, password, and alias values to match the values for your certificate. Then
compile and run [Link].
© 2011 ITCourseware, LLC Rev 5.1.4 Page 205
Advanced Java Programming
Certificate Chains
One entity might issue a certificate of another entity, who, in turn, might issue a
certificate for another, and so on.
¾ The resulting certificate chain validates all its entities based on the
original issuer's authority.
When you generate a keypair, keytool creates a self-certified certificate for your
public key.
¾ That is, your own private key is used to sign a statement that your public
key belongs to you.
1. Use keytool -certreq to export your public key into a Certificate Signing
Request file:
3. From the CA, you'll receive a certificate file, signed by them, containing
your public key.
Recipients of your signed code can import your certificate into their keystores,
and grant permissions to your code based on how much they trust you . . .
¾ The default keystore is in the .keystore file in your home directory, but
you can have many keystores, with different file names, in different
directories.
Java provides three different algorithms for storing keys, and you can specify
which a given keystore uses whenever you create or alter a keystore.
¾ The default, JKS, provides the weakest encryption and can not store
secret keys (used for encryption).
You can manage keys and certificates with the keytool utility or through the key
management API.
[Link]
import [Link].*;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Try It:
Change [Link] to use your keystore file and password. Compile and run it to see all of the
aliases associated with your keystore/password combination.
You can specify security policies for your VM that are applied according to the
signer of the JAR containing programs you run.
In policytool, use Change Keystore under the Edit menu to specify the path to
your keystore file.
¾ To add policy entries for JAR signers, enter their aliases (as listed in your
keystore) in the Signed By field.
Hands On:
Use appletviewer to run the WriteFile applet from your signed JAR: appletviewer [Link].
Try writing a file. You should get a security exception, because, by default, the applet does not have the
appropriate FilePermission.
To fix this you will need to set up the keystore first. Start policytool, select EditÆChange KeyStore and
enter the location of your .keystore file and jks for the type. Use [Link] and Settings/
student/.keystore for the keystore URL on Windows.(Use your login, rather than "student.")
Now, grant write permission to all files in the working directory for this chapter, to code signed by you (that
is, use the alias you used earlier to sign your JAR in the SignedBy field in the policy entry). For the Target
Name, list the chapter directory, followed by an *.
After saving your policy file, run appletviewer again and try writing a file.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 211
Advanced Java Programming
¾ The parts of the [Link] package, and its sub packages, that deal
with key management, certificates, digital signatures, message digests, and
ciphers are part of the JCA.
¾ The [Link] package includes other classes that deal with policies,
authentication and authorization.
¾ You can have multiple providers installed, and select which you want to
use at runtime.
The JCA was first released with JDK 1.1, when it included support for digital
signatures and message digests.
¾ The SunJCE is now bundled with the JDK, but older versions required a
separate download.
Labs
Extract the META-INF/ directory from the signed .jar file you created in this chapter, and
examine the contents.
(Solution: [Link])
Create another keypair, giving it a unique alias, using keytool. List the contents of your keystore.
(Solution: [Link])
Sign the same .jar file you signed earlier, but this time use your new keypair. Afterwards,
extract the META-INF/ directory again. What files are there? Compare the contents of the
signature (.SF) files.
(Solution: [Link])
Using the -keystore keystorefile option to keytool, create a new keypair in a temporary keystore
file in the current directory. List the contents of the new keystore. Now use keytool's -export
option to export the public key from your new keypair — be sure to use the -file option as well, or
the key will be exported in binary form to your terminal screen.
Now, import the key you just exported, into your default keystore. List the contents of your default
keystore.
Write a program to iterate through the aliases in your keystore, displaying whether each is a private
key entry or a certificate entry. If it is a key, display the private key. Display the beginning and
ending validity period dates for each entry.
(Solution: [Link])
Objectives
Encrypt data using the [Link]
package.
Cryptography Concepts
Cryptography is based on the use of a key to encrypt and decrypt sensitive data.
¾ Symmetric cryptography uses the same secret key to encrypt and decrypt.
¾ Asymmetric cryptography uses one key to encrypt the data and a second
key for decryption.
The Cipher class provides methods to encrypt and decrypt arrays or streams.
You need to provide three values when creating an instance of the Cipher class:
The Java Cryptography Extension (JCE) is bundled with the JDK platform, adding support for ciphers,
key agreement, and authentication codes. JCE 1.2 comes standard with the provider SunJCE, which
supplies many services, including various encryption algorithms, a padding scheme, and key generator
implementations.
Encryption Keys
The algorithm in use and secrecy of the private key determine the effectiveness
of the key encryption.
¾ Public/Private (Asymmetric) keys encrypt with one key and decrypt with
the other.
[Link] uses a KeyGenerator to create a secret key object and store it in a file.
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
Use the DES algorithm to
import [Link]; generate the key.
Try It:
Compile and run [Link] to use a KeyGenerator to create a file containing a secret key. This
file will be used by other applications in this chapter.
Cipher Algorithms
There are a variety of algorithms that can be used to encrypt and decrypt data.
The SunJCE provides several algorithms you can use when creating your
Cipher class.
¾ Triple-DES uses a key length of 168-bits (three 56-bit DES keys) for three
rounds of encryption and decryption.
This algorithm has never been broken, but also requires more time
to perform the encryption and decryption.
A cipher splits the text into two halves and one half is encrypted
during each round using a subkey.
There are also algorithms that use 256-bit key encryption. This is known as Unlimited Strength
Encryption. The jurisdiction policy files shipped with the JDK allow "strong," but limited
cryptography to be used. This is due to import control restrictions of some countries. To perform Unlimited
Strength Encryption, you must download jurisdiction policy files from Sun which can be found at:
[Link]
Modes are provided in addition to the algorithm to further alter the data being
encrypted.
Modes help to break apart patterns that might be encountered in the original
data.
Five different modes have been specified in the SunJCE for you to utilize.
¾ Cipher Block Chaining (CBC) uses the input from one block to encrypt
the next block of data, which helps to hide repeating data that might occur
in your input.
Padding is used to fill the entire block size that needs to be encrypted.
The number of bits that make up a block can vary with CFB, OFB, and PCBC. Append a number to the
end of the mode name to indicate the number of bits you wish to make up a block. The number must be a
multiple of eight. For example, with CFB8, the block size would be one byte, instead of the default eight
bytes.
Use the static getInstance() method of the Cipher class to generate Cipher
objects.
Cipher c1 = [Link]("DES");
Cipher c2 = [Link]("DES/CBC/PKCS5Padding");
Once you have obtained a Cipher object, the init() method must be called to
determine encryption or decryption, and provide a key.
¾ The key type must match the type of encryption being used.
[Link](Cipher.ENCRYPT_MODE, key);
[Link](Cipher.DECRYPT_MODE, key);
Some of the algorithms require you to provide an initialization vector (IV) for
decryption.
¾ The initialization vector is available after the data has been encrypted.
Hands On:
Create a new program called [Link]. In this program, you will create a Cipher object that will
use the DES algorithm with ECB mode and PKCS5Padding. You will use the KeyGenerator class to
create a key to initialize the Cipher.
import [Link].*;
import [Link];
The Cipher class has several overloaded methods that you can use to encrypt or
decrypt data.
¾ If the number of bytes passed to update() does not fill an entire block,
then the odd bytes are held in an internal buffer.
¾ An offset and length can also be provided to limit the extent of the data
being modified.
The doFinal() method is the last method you call for an encryption or
decryption.
¾ Any odd bytes left in the buffer are either padded, if PKCS5Padding is
used, or an IllegalBlockSizeException is thrown.
Hands On :
Now call the update() and doFinal() methods to encrypt and then decrypt a String.
import [Link].*;
import [Link];
[Link](Cipher.DECRYPT_MODE, theKey);
Try It:
Compile and run [Link]. Why do the decrypted Strings look different from the original
Strings?
¾ The Cipher will encrypt or decrypt, depending on how the Cipher was
initialized.
Often you will use this class in conjunction with other stream classes as a filter
for encryption/decryption.
Any padding is performed when the flush() method is invoked before the
closing of the stream.
[Link]
import [Link].*;
import [Link];
import [Link];
import [Link];
Try It:
Run [Link] to encrypt [Link] and place the encrypted data in [Link].
Again, you will generally use this class in conjunction with other stream classes
as a filter for encryption/decryption of the input stream.
[Link]
import [Link].*;
import [Link];
import [Link].*;
Cipher c = [Link]("DES/ECB/PKCS5Padding");
[Link](Cipher.DECRYPT_MODE, theKey);
Try It:
Compile and run [Link] to decrypt and view the contents of [Link].
This allows the password to be shared verbally between the encryption side and
the decryption side.
¾ The salt and iteration work with the password to create a more secure
encryption.
¾ The more random the bytes and the higher the iteration count, the more
secure the encryption.
¾ Use a PBEParameterSpec object to hold the salt and iteration values for
the cipher initialization.
[Link]
...
Prompt for password
public class PasswordEncrypt {
to encrypt data.
public static void main(String args[]) {
try {
BufferedReader br = new BufferedReader(
new InputStreamReader([Link]));
[Link]("Enter Password for Encryption: ");
String password = [Link]();
[Link]();
[Link]();
}
catch (Exception e) {
[Link]("Error : " + [Link]());
}
}
}
Try It:
Compile and run [Link]. Open [Link] in a text editor to see the results.
The Cipher class provides special methods to encrypt and decrypt Key objects.
¾ The wrap() method takes the Key object and returns the key encrypted as
a byte array.
¾ The unwrap() method takes the encrypted byte array, the algorithm used
to generate the original key, and the key type, and returns a Key object.
[Link]
...
public class Producer {
...
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("[Link]"));
Key key = (Key) [Link]();
[Link]();
KeyGenerator kg = [Link]("DES");
SecretKey sk = [Link]();
Cipher c = [Link]("DES/ECB/NoPadding");
[Link](Cipher.WRAP_MODE, key);
Encrypt key into a byte array.
byte[] wrappedKey = [Link](sk);
DataOutputStream dos = new DataOutputStream (
new FileOutputStream("[Link]"));
[Link](wrappedKey, 0, [Link]);
[Link]();
...
}
[Link]
...
public class Consumer {
...
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("[Link]"));
Key key = (Key) [Link]();
[Link]();
Cipher c = [Link]("DES/ECB/NoPadding");
[Link](Cipher.UNWRAP_MODE, key);
Try It:
Compile both [Link] and [Link]. Run [Link] to create a new key and use it to
encrypt a file. Run [Link] to read the new key and decrypt the file.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 237
Advanced Java Programming
Sealed Objects
The encrypted content can later be decrypted (with the corresponding algorithm
using the correct decryption key) and deserialized, yielding the original object.
¾ Use the getObject() method with a Cipher to deserialize and decrypt the
encapsulated object.
[Link]
...
class StudentGrade implements Serializable {
private String studentName;
private float studentGPA;
Cipher c = [Link]("DES/ECB/PKCS5Padding");
[Link](Cipher.ENCRYPT_MODE, key);
SealedObject so = new SealedObject(sg, c);
[Link](Cipher.DECRYPT_MODE, key);
StudentGrade grade = (StudentGrade) [Link](c);
[Link](grade);
}
...
}
}
Try It:
Compile and run [Link] and open [Link] in a text editor. Notice that all of the data in
the object is not visible.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 239
Advanced Java Programming
Labs
Write a program that reads text from the standard input (keyboard) and saves the text in
encrypted format in a file. The key used to encrypt the file needs to be saved in a second file.
(Solution: [Link])
Write a program that reads and decrypts the file written in the previous exercise.
(Solution: [Link])
Create a program to decrypt the file that was created by the [Link]
program.
(Solution: [Link])
Create a program that can store and list username/password combinations for websites that
a user may register for. To add an entry, prompt the user for the website name, the username,
and the password. Encrypt the data using a password to prevent unauthorized access.
(Solution: [Link])
Objectives
Authenticate a user with JAAS.
Authentication is the process of identifying a user and verifying they are who
they say they are.
Once the user is authenticated, authorization specifies what they can do within
the system.
In Java, you authorize code to access resources through policy files, and you
authorize users the same way.
JAAS Overview
¾ Prior to Java 1.4, JAAS was a separate download, but it is now packaged
with the JDK.
¾ JAAS is a different mechanism than the one used in J2EE app servers.
To implement JAAS, you must write some program code, as well as set up a
policy file and a login configuration file.
¾ These classes are defined in the [Link] package and its sub
packages.
¾ Some of the classes are extended by service providers and exist in other
packages.
JAAS extends the policy file to authorize users to perform specific operations.
¾ Prior to version 1.4, a separate policy file was required for JAAS to
support the additional syntax.
LoginContext
Once you have the LoginContext, call the login() method to authenticate the
current user.
[Link]
import [Link];
import [Link];
import [Link];
try {
lc = new LoginContext("Salaries");
[Link]();
Subject subject = [Link]();
...
}
[Link]();
}
catch (Exception e) {
[Link]();
}
}
}
[Link]
...
Salaries {
[Link] required;
};
...
The control flag in the login configuration file can have a value of required, sufficient, requisite, or
optional. The most common value is required, which means that the security module must be executed and
passed. It is possible to list multiple authentication modules for a configuration entry, and that is when the
other three options come into play.
When there are multiple security modules, they are executed in order from top to bottom.
If the control flag is optional, the user may fail that module, but must pass at least one other module in the
configuration entry.
If the control flag is sufficient, no more modules will be executed, unless they are required.
If the control flag is requisite, remaining modules will be executed, but unless they are required they can
be failed.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 249
Advanced Java Programming
You can retrieve the Set of Principals from the Subject with the
getPrincipals() method.
¾ From the Principal you can call getName() to find out who the user is.
¾ You can also get the Subject's public and private credentials.
Once you retrieve the Subject from the LoginContext, use it to run privileged
code.
¾ Call the static [Link]() method, passing the Subject object and a
PrivilegedAction.
¾ Define the code you want an authorized user to execute in this method.
¾ The run() method executes code that uses some resources that are
protected by Permissions.
[Link]
...
public class ShowSalaries {
public static void main(String[] args) {
...
Subject subject = [Link]();
Object o = [Link](subject, new SalariesAction());
...
}
}
[Link]
...
public class SalariesAction implements PrivilegedAction {
private static NumberFormat nf = [Link]();
Hands On:
Compile [Link] and [Link]. Because these classes require different permissions,
we need to put them in different codebases. We could either do that by putting them in different directories,
or by putting them in different JARs. There is a manifest file in the chapter directory that specifies the main
class and class path for the ShowSalaries application, so let's create JARs. First create the application
JAR: jar cvfm [Link] [Link] [Link]. Then create the library JAR: jar cvf [Link]
[Link].
¾ You can also get third-party login modules or write your own.
The NTLoginModule allows your Java program to take advantage of the user's
Windows identity, so they do not have to login separately to your application.
NTJAAS {
[Link] required
}
When you create your LoginContext, you do not have to specify a Callback,
because the user has already logged in.
When you retrieve the Subject, you can see the various identities of the user by
iterating through the Set of Principals.
¾ NTSidUserPrincipal, NTSidDomainPrincipal,
NTSidGroupPrincipal, and NTSidPrimaryGroupPrincipal contain
the Windows security identifier of the user ID, domain, groups, and
primary group, respectively.
Page 252 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 12 Java Authentication and Authorization Service
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
try {
context = new LoginContext("NTJAAS");
[Link]();
subject = [Link]();
Set<Principal> principals = [Link]();
for (Principal p : principals) {
[Link]("Principal type: " +
[Link]().getName() + " name: " + [Link]());
}
}
catch (Exception e) {
[Link]();
}
}
}
[Link]
NTJAAS {
[Link] required;
};
...
[Link]
grant codeBase "[Link] {
permission [Link];
}
Try It:
If you are on a Windows system, this program will show you the user identities. Compile [Link].
When you run it, you need to install a SecurityManager, and specify a policy file and login configuration
file:
java -[Link] -[Link]=[Link]
-[Link]=[Link] NTJAAS
A JAAS program is divided in (at least) two parts: the main code and the
privileged action.
¾ Each part has different permission requirements, so your policy file will
need (at least) two codeBase entries.
The privileged action requires permission for whatever secured resource the
user needs to access.
¾ The privileged action policy also specifies the principal type and name of
who can access the resource.
Hands On:
Start policytool. If you have a .[Link] in your user directory, that will automatically be loaded. Select
FileÆNew. Select the Add Policy Entry button. For the CodeBase, enter [Link]
[Link].
Select the Add Permission button. In the Permission dropdown list, select AuthPermission. For the
Target, select doAs. Press OK.
Select Add Permission again. For the Permission select AuthPermission. For the Target, select
createLoginContext.<name>. In the text box to the right, replace <name> with Salaries. Press OK.
Select Add Permission a third time. For the Permission select FilePermission. For the Target, enter
[Link]. For the Action, select read. Press OK. Press Done to complete this policy entry.
Select the FileÆSave As menu option, and save this as [Link] in your current chapter
directory.
Note: The CodeBase URLs require an absolute path for a .jar file. If your current chapter directory is
something else, adjust the CodeBase appropriately.
Try It:
You can now run the ShowSalaries program:
KeyStoreLoginModule
¾ By default, it uses the .keystore file in the user directory and the default
keystore protocol, but you can specify others in the login config file.
[Link] required
option=value, option2=value2...;
[Link]
...
CallbackSample {
[Link] required;
};
Hands On:
The example program that we're going to run in a couple of pages requires a certificate in your keystore. If
you have already created one, you need to find out the X.500 distinguished name, which includes the user's
name, organization, city, state, and country. To find the distinguished name, edit the [Link]
program in this chapter directory, replacing the alias, keystore file location, and keystore password
with your values. Then compile and run the program, making a note of the Certificate subject string.
If you do not have a keystore or a certificate, run keytool -genkey -alias yourname. Enter the requested
information, then compile and run [Link] as discussed above.
Now, run policytool. Select FileÆNew. Select the Add Policy Entry button. For the CodeBase, enter
[Link]
Select the Add Permission button. In the Permission dropdown list, select AllPermission. Press OK.
Press Done to complete this policy entry.
Select the FileÆSave As menu option, and save this as [Link] in your current chapter
directory.
Note: The CodeBase URLs require an absolute path for a .jar file. If your current chapter directory is
something else, adjust the CodeBase appropriately.
Callbacks
Callbacks are the mechanism that the LoginModule uses to get information
about the user from the application.
¾ They can also provide information from the LoginModule to the user.
When you call login() on the LoginContext, the LoginContext calls login() on
the LoginModule specified in the login configuration file.
¾ Within the method, iterate through the Callback[], prompting the user for
the requested information.
Page 258 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 12 Java Authentication and Authorization Service
Application
JAAS
login()
login()
LoginContext LoginModule
new new
NameCallback PasswordCallback
There are many types of callbacks, and they all implement the Callback
interface.
¾ After the user enters their name, call setName() to add it to the
NameCallback object.
¾ As with the NameCallback, your handler should display the return value
from the getPrompt() method.
[Link]
...
public class LoginHandler implements CallbackHandler {
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
for (Callback cb : callbacks) {
if (cb instanceof NameCallback) {
NameCallback ncb = (NameCallback) cb;
[Link]([Link]() + " ");
BufferedReader in = new BufferedReader(
new InputStreamReader([Link]));
String name = [Link]();
[Link](name);
}
else if (cb instanceof PasswordCallback) {
PasswordCallback pcb = (PasswordCallback) cb;
[Link]([Link]() + " ");
BufferedReader in = new BufferedReader(
new InputStreamReader([Link]));
String password = [Link]();
[Link]([Link]()); Handlers for TextOutputCallback
} and ConfirmationCallback used by
... KeyStoreLoginModule.
else {
throw new UnsupportedCallbackException(cb);
}
}
}
}
[Link]
...
lc = new LoginContext("CallbackSample", new LoginHandler());
...
CallbackSample is very similar to ShowSalaries, the only difference being we pass a LoginHandler
to the LoginContext constructor.
Try It:
Compile [Link] and [Link]. This program uses the SalariesAction from a
previous example, so you should already have compiled that and packaged it in a .jar.
Create your application .jar file:
jar cvfm [Link] [Link] [Link] [Link]
¾ You can use this class to work with standard policies as well as JAAS
policies.
Retrieve the permissions from the Policy with the getPermissions() method.
[Link]
...
public class ShowPolicies {
public static void main(String[] args) {
LoginContext lc = null;
try {
lc = new LoginContext("Salaries");
[Link]();
Subject subject = [Link]();
Principal[] principals =
[Link]().toArray(new Principal[0]);
Enumeration<Permission> e = [Link]();
while ([Link]()) {
Permission p = [Link]();
[Link]("Permission: " + p);
}
[Link]();
}
...
}
}
[Link]
grant codeBase "[Link] {
permission [Link] "doAs";
permission [Link] "[Link]";
permission [Link] "[Link]", "read";
permission [Link] "getPolicy";
};
Try It:
Compile and run [Link] with the [Link] file: java -[Link]
-[Link]=[Link] -[Link]=[Link] ShowPolicies
Labs
In this chapter directory there is a sample login module called EgLoginModule. It uses a
NameCallback and a PasswordCallback to authenticate a user, then adds an EgPrincipal to
the Subject. Write a callback handler to handle the callbacks from EgLoginModule.
(Solution: [Link])
Change the [Link] program from this chapter directory to use your callback
handler from . Create a login config file for your program that uses [Link].
(Solutions: [Link], [Link])
Create a policy file that grants AllPermission to your .jar file from and
[Link] "[Link]", "read" to [Link]. Because policytool does not know
about EgPrincipal, you will need to edit the policy file manually. The users are defined in the users
file in the chapter directory, which is a standard Java properties file.
(Solution: [Link])
Run your program, entering the appropriate user name and password from the users file.
(Solution: [Link])
Objectives
JNDI uses service providers which interface with these and other systems.
In some of the examples in this chapter we will use JNDI to access the RMI Registry. For most basic
RMI applications, you would probably use the [Link] or [Link]
classes instead of JNDI to access a registry. Using JNDI can provide some additional flexibility with regards
to configuration, but more interesting is the ability of a client application to dynamically use a completely
different service, such as an LDAP directory, to locate remote objects. This strategy is especially useful for
generic applications such as frameworks.
Setup:
To run the examples that use RMI in this chapter, you will first need to start the RMI Registry, then run
the server application to create some remote objects. Compile the source files, then execute the following
commands, each in their own window (make sure you run these in the directory with the class files or
otherwise set an appropriate classpath):
rmiregistry
java Server
Note:
The examples for directory services require access to LDAP and DNS services. If your firewall allows
access to such services, then you should be able to access the public services used in the sample code.
There are lists of some public services you can also try in public_ldap_servers.txt and
public_dns_servers.txt, both extracted from internet searches.
JNDI gives you a common set of tools for dealing with naming and directory
services, including:
¾ Searching.
¾ You can pass it a Hashtable to select the service provider and to provide
other necessary information.
The InitialContext constructors will also look at the system properties or applet parameters, if
appropriate, and then at the file [Link] (located anywhere in the classpath) for the properties
needed to define and initialize the context. This provides a convenient, standardized mechanism for
applications to allow the end-user to configure the application to use an arbitrary naming service.
[Link]=[Link]
[Link]=rmi:///
Given the above [Link] file, the client code could create an InitialContext using the no-
argument constructor.
Naming Operations
¾ lookup() returns an Object, which you can cast to the type of object you
are looking up.
AccountFactory fact =
(AccountFactory)[Link]("[Link]");
Object o = [Link]([Link]());
The name passed into these methods can also be a full URL, potentially referring
to a separate context or even a different service provider.
NamingEnumeration<NameClassPair> stoogesOrg =
[Link]("ldap://[Link]/o=stooges");
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Try It:
Run Lister without parameters to list the names bound in the RMI Registry.
Bindings
You can create, replace, delete, or rename a binding using Context methods.
[Link]("NewObject", newObject);
[Link]("NewObject", otherObject);
[Link]("NewObject", "OtherObject");
[Link]("OtherObject");
¾ This exception is also thrown if the type of the object is not supported by
the service provider.
A binding is the association of a name with an object. Managing name-to-object bindings is the purpose of a
naming system.
The Binding class represents a binding as a String (the name) and an Object (the object). A Binding also
includes the class name of the bound object. A NameClassPair encapsulates a bound name and just the
class name of the bound object without a reference to the object.
For example, when using the RMI context service provider, the name will be an arbitrary name bound to the
object by the server, the class name will be [Link], and the object will be an instance of the
generated stub class which implements the remote interface and is connected to the remote object. Note
that the class name is not particularly useful to an RMI application; you will need to examine the object itself
to determine its class.
Attributes
[Link]
...
public class DirLookup {
public static void main(String args[]) {
...
try {
InitialDirContext ctx = new InitialDirContext(env);
Try It:
[Link] by default looks up the command-line argument in the top-level entry in the test LDAP
server at [Link]:
You can also query another directory service by passing a URL on the command-line:
Directory Operations
You can also search a directory service using attribute names and values.
[Link]
...
public class StoogeSearch {
public static void main(String args[]) {
Hashtable<String, String> env = new Hashtable<String, String>();
[Link](Context.INITIAL_CONTEXT_FACTORY,
"[Link]"); We search for an entry
[Link](Context.PROVIDER_URL, with an attribute named
"ldap://[Link]/o=stooges"); sn with value Howard.
try {
InitialDirContext ctx = new InitialDirContext(env);
NamingEnumeration<SearchResult> results =
[Link]("ou=MemberGroupA", searchParms);
while ([Link]()) { The search will only
search within the
StringBuilder buf = new StringBuilder();
subcontext of
SearchResult aResult = [Link]();
organizational unit
[Link]([Link]()).append('\n'); MemberGroupA.
Try It:
Compile and run [Link] to see Moe Howard's attribute values.
[Link](Context.PROVIDER_URL,
"dns://[Link]/ dns://[Link]/");
A — A host's IP address.
MX — The host's mail exchanger(s), with preference values.
CNAME — Canonical name for a host.
SOA — Start-of-authority for a DNS zone.
PTR — Reverse DNS records.
JNDI in J2EE
A J2EE application might also use JNDI to integrate with any number of existing
systems.
DNS
LDAP
etc.
Application Server
Web Container
EJB Container
look
up(e
jb) Naming jb)
up(e loo
look Service ku
p(d
) ata Session Bean
Servlet
ource loo
ku
sou
tas p(j rce
a )
ku p(d ms
-de
loo sti Entity Bean
na
Servlet tio
n)
Message-Driven
Bean
Labs
Write a program that uses JNDI to locate the RateInfo object registered in the RMI Registry,
using RateInfo as the lookup name, and displays the rate information by calling the getInfo()
method. Make sure that the rmiregistry and the Server application are running as described at the
beginning of the chapter.
(Solution: [Link])
Write a program that locates all ServerStatus objects registered in the RMI Registry using JNDI
and displays their statuses by calling their getStatus() method. Looking up every object in the
registry could be expensive, so take advantage of our naming convention to identify which objects
are ServerStatus objects before calling lookup(). Make sure that the rmiregistry and the Server
application are running as described at the beginning of the chapter. (Hint: Run the Lister example
application to observe the naming convention. This will also show you how many results you should
get.)
(Solution: [Link])
(Optional. Requires DNS nameserver.) Write a program that takes a hostname as a command-line
argument, and uses JNDI to look up and print out the DNS A record for the hostname.
(Solution: [Link])
(Optional. Requires DNS nameserver.) Modify your solution to so that, after the hostname, any
number of resource record types can be listed on the command-line. Print all such resource records
for the named host.
(Solution: [Link])
Objectives
Describe the Java API for XML Processing
(JAXP).
Java API for XML Processing (JAXP) enables Java applications to parse and
transform XML documents without constraint from a particular XML
implementation.
java -[Link]=[Link]
¾ Event-based parsing using Simple API for XML (SAX) treats the contents
of the XML document as a series of events and calls various methods in
response to those events.
¾ You can create a new XML, HTML, or text document using an XSLT
Processor.
¾ Just as SAX and DOM parsers can be switched at runtime, so can XSLT
processors.
JAXP is bundled with the JDK, standard edition, version 1.4 or later.
App
JAXP
Parsing Transformation
SAX DOM
Xerces Xalan
Xerces and Xalan are Apache implementations that come bundled with Java 5.
¾ The XML content is seen as a series of events, triggering the SAX parser
to call various handler methods.
¾ The entire document does not need to be loaded into memory at one time,
thus enabling SAX to parse larger documents.
The DefaultHandler class implements all the basic SAX interfaces and
provides default implementations.
Application
class MyHandler
extends DefaultHandler
setDocumentLocator()
startDocument()
endDocument()
XML Data SAX Parser
startElement()
characters()
. .
. .
. .
There are four basic steps when using JAXP with SAX:
4. Call the parser's parse() method, passing a reference to the XML data and
the handler implementation class.
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
try {
SAXParser parser = [Link]();
DefaultHandler handler = new SAXHandler();
[Link](new File(args[0]), handler);
}
catch (Exception e) {
[Link] ("ERROR " + e);
}
}
}
[Link]
<?xml version="1.0"?>
<garage>
<car miles="0">
<make>Porsche</make><model>911</model><year>2001</year>
</car>
<car miles="250000">
<make>VW</make><model>Beetle</model><year>1974</year>
</car>
<van miles="50000">
<make>Ford</make><model>E350</model><year>2000</year>
</van>
</garage>
Try It:
Compile [Link] and run it using [Link] as the input file to list the make and model of each
car or van.
ch — the character array that contains the actual character data that was
found.
start — the starting point in the array.
length — the number of characters to read from the array.
[Link]
import [Link];
import [Link]; DefaultHandler provides
import [Link]; stubs for all SAX event
handler interfaces.
class SAXHandler extends DefaultHandler {
private boolean printChars = false;
if ([Link]("make")) {
[Link]("\n");
}
if ([Link]("make") || [Link]("model")) {
[Link](qName + " : ");
printChars = true;
}
}
public void characters(char ch[], int start, int length)
throws SAXException {
if (printChars) {
String s = new String(ch, start, length);
[Link](s);
}
printChars = false;
}
}
Introduction to DOM
The parser creates an internal, tree-like data structure containing objects that
represent the various parts of the XML document.
¾ The classes that make up the objects in the tree implement various
interfaces specified by the World Wide Web Consortium (W3C).
After the parser is finished building the tree structure, it returns a reference to
the top node of the tree, called the Document.
An advantage of DOM is the ability to manipulate the document after it has been
parsed.
¾ You can also use DOM to create a new document from scratch.
Parsing an XML document using JAXP and DOM involves four steps:
DocumentBuilderFactory factory =
[Link]();
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
DocumentBuilderFactory factory =
[Link]();
try {
DocumentBuilder builder = [Link]( );
Document document = [Link]( new File(args[0]) );
[Link]("Vehicles In My Garage\n");
searchForVehicles(document);
[Link]("Garage Door Closed");
}
catch (Exception e) {
[Link](e);
}
}
...
}
Try It:
Compile and run [Link] to list the make and model of each vehicle in the garage using a
DOM parser.
DOM specifies twelve types of objects that can be included in the DOM data
structure.
From the Document node you can traverse the structure by calling various
methods.
¾ You can append child nodes and manipulate attributes from the Element
node.
Text nodes are children of Element nodes and contain the actual text.
«interface»
Node
«interface»
NodeList «interface» «interface»
Comment Text
«interface»
NamedNodeMap «interface»
CDATASection
Note:
All interfaces are in the [Link] package.
[Link]
...
public static void searchForVehicles(Document doc) {
NodeList list = [Link]("car");
processVehicleList(list);
list = [Link]("van");
processVehicleList(list);
}
public static void processVehicleList(NodeList autoList) {
for (int i = 0; i < [Link](); i++) {
Node auto = [Link](i);
NodeList autoFeatures = [Link]();
if ([Link]() == Node.ELEMENT_NODE) {
Element feature = (Element) featureNode;
String name = [Link]();
if ([Link]("make") || [Link]("model"))
[Link] (name + " : " +
[Link]().getNodeValue());
}
}
Element has various methods
[Link](); for querying node content.
}
}
}
Validation
2. An XML Schema defines the valid structure, as well as content types for
XML documents.
The ErrorHandler interface provides three methods to deal with different types
of errors.
¾ error() is called to report that the XML document will not validate
against the DTD or Schema.
[Link](true)
SchemaFactory f =
[Link](XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = [Link](new File("[Link]"));
[Link]
...
public class SAXValidator {
public static void main(String args[]) {
if ([Link] != 2) {
[Link](
"Usage: java SAXValidator [Link] [Link]");
[Link](1);
}
try {
SchemaFactory schemaFactory =
[Link](XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = [Link](new File(args[1]));
Try It:
Run SAXValidator using [Link] and then [Link] passing in [Link] as the second
parameter.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 303
Advanced Java Programming
Transformation
XSLT can transform an XML document into an HTML file, a text file, or another
XML document.
¾ XSLT is the W3C standard for creating XML documents that contain
transformation templates.
JAXP contains several interfaces and classes used to simplify the transformation.
TransformerFactory tFactory =
[Link]();
Transformer transformer =
[Link](xslSource);
[Link](xmlSource, xmlResult);
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link]
<xsl:stylesheet xmlns:xsl="[Link]
version="1.0">
<xsl:template match="/">
<html><body><div align="center">
<table border="1">
<xsl:apply-templates select="garage/*"/>
</table>
</div></body></html>
</xsl:template>
<xsl:template match="car|van">
<tr>
<td><xsl:value-of select="year"/></td>
<td><xsl:value-of select="make"/></td>
<td><xsl:value-of select="model"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Try It:
Run [Link] and then open [Link] in a browser.
Labs
Create an application that will use SAX to read through [Link] and count the number of
vehicles that have less than 20,000 miles.
(Solution: [Link])
Create an application that will parse [Link] into a DOM structure. Create a new car Element
node using Document's createElement() method. Append the Element as a new child of the
garage Element. Create three new Element nodes for make, model, and year, adding them as
children of your new car Element. Create text nodes using Document's createTextNode()
method and add them as children to make, model, and year with appropriate data.
(Solution: [Link])
Modify your program from to send your DOM structure to a Transformer to run against
[Link], creating an HTML file. Open the HTML file in a browser to view your results.
(Solution: [Link])
Objectives
Your application may need functionality not available in the Java language, its
standard classes, or any other available classes.
In addition to general support for native methods, Java provides the Java Native
Interface (JNI); a specification and a utility library for C/C++ native-method
programmers.
¾ Utility functions.
The details of writing and compiling native method implementations vary with
the native language and operating platform.
6. Create a Java program that instantiates an object of the wrapper class and
calls the native methods.
You might think you would implement the native method first, then integrate it with your Java code; but,
since you need to compile and test it from your Java code, and use javah to create headers before you can
compile your native code, you will probably set up your Java application (or a test app) first (as we will in
this chapter), then complete the native implementation.
javah Java VM
CCCheck.h
ClassLoader
#include <jni.h>
JNIEXPORT jint JNICALL Java_CCCheck_validCC [Link](...)
(JNIEnv *, jobject, jstring, jint);
Define a new Java class; the native methods will be methods of this class.
Declare native methods just like abstract methods, replacing abstract with
native.
Use a static initializer in this wrapper class to load the library containing the
compiled native method implementations.
¾ [Link]() will load the appropriate .dll or .so library file for
dynamic linking.
static {
[Link]("CCCheck");
}
[Link]
public class CCCheck {
public native int validCC(String ccNumber, int digitCount);
static {
[Link]("CCCheck");
} Don't add the .dll or .so on
} the end of the file name.
Hands On:
Create [Link]:
javac [Link]
Using javah
The javah utility, part of the standard JDK, creates a C header file (.h file) from
the class definition of the wrapper class.
javah produces a .h file with the function prototype for the function
implementing the Java native method.
The function prototype will use datatypes defined in header files under the JDK's
include/ directory.
¾ The #include <jni.h> at the top includes all the necessary headers.
As the comment says, do not edit this .h file — not because you will break it,
but because you are likely to regenerate it with javah as you enhance your class.
Use the function definition from the .h file in your C implementation by copying
the prototype from .h, and adding variable names.
Hands On:
javah CCCheck
This produces the C header file CCCheck.h, which contains the function prototype for the C
implementation of the validCC() Java method:
CCCheck.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class CCCheck */
#ifndef _Included_CCCheck
#define _Included_CCCheck
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: CCCheck
* Method: validCC Use this function
* Signature: (Ljava/lang/String;I)I prototype in your C
*/ implementation.
JNIEXPORT jint JNICALL Java_CCCheck_validCC
(JNIEnv *, jobject, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif
Create a C program with the definition of the function declared in the .h file
created by javah.
Note that JNI declares two parameters for every native function:
LegacyCCCheck.c
#include <string.h>
#include <stdio.h>
CCCheck.c
#include "CCCheck.h" These are the two
#include <string.h> parameters defined
#include "LegacyCCCheck.h" in the Java method.
int valid = 0;
strcpy(cardNumber, str);
valid = ccCheck(cardNumber, count);
(*env)->ReleaseStringUTFChars(env, ccNumber, str);
return valid;
}
Compilation
Use your preferred native compiler to build a shared library file from your
implementation code.
¾ The compiler's include path must have the standard and the platform-
specific Java include directories.
On UNIX/Linux:
JAVA_HOME=/opt/j2sdk_nb/j2sdk1.5.0_03
export JAVA_HOME
gcc -shared -o [Link] -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
nativeCCCheck.c
Hands On:
On Windows:
JAVA_HOME=c:\Program Files\java\jdk1.5.0_03
Distribution
¾ The path to the shared library file must be known to the VM — for
example, listed in the PATH on Windows, or in LD_LIBRARY_PATH
or LD_RUN_PATH on UNIX.
You can instead specify on the command-line where Java will look for the
library by setting the [Link] system property.
¾ The following command example will look for the library in the current
directory:
Hands On:
On UNIX/Linux:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
export LD_LIBRARY_PATH
On Windows:
PATH=%PATH%;.
Simply instantiate an object of the wrapper class and call its methods normally.
[Link]
public class CCCheckTest {
public static void main (String args[]) {
int valid = 0;
String testNumber = "12344";
CCCheck checker = new CCCheck();
testNumber = "12341";
valid = [Link](testNumber,[Link]());
[Link]("Number " + testNumber + " is " + valid);
}
}
[Link]
public class CCCheck {
public native int validCC(String ccNumber, int digitCount);
static {
[Link]("CCCheck");
}
}
Hands On:
JNI
The JNI defines native types that map to Java primitives, references, and
objects.
Version Information
Class Operations
Exceptions
Global and Local References
Object Operations
Accessing Fields of Objects
Calling Instance Methods
Accessing Static Fields and Methods
String and Array Operations
Each native method call starts with a pointer to the JNI program interface, which
is a list of function pointers to standard JNI functions.
¾ For example, in C you can retrieve the current version number with the
following call to the GetVersion() function:
jint v = (*env)->GetVersion(env);
jint v = env->GetVersion();
The jni.h file provides java compatible types for use in native C code when passing information between C
and Java.
Re fe re nce Type s
JNI Type Hie rarchy Java Type
jobje ct Any Java Object
jclas s [Link] s
js tring [Link]
jthrowable [Link]
jarray Java arrays
jbyte aray byte []
jchararray char[]
js hortarray s hort[]
jintarray int[]
jlongarray long[]
jfloatarray float[]
jdouble array double []
jboole anarray boole an[]
jobje ctarray Any Object array
Passing Arguments
Parameters you declare for native methods in your wrapper class will be
declared as JNI types by javah.
[Link]
public class CCCheckTest {
public static void main (String args[]) {
int valid = 0;
String testNumber = "12344";
CCCheck checker = new CCCheck();
[Link]
public class CCCheck {
public native int validCC(String ccNumber, int digitCount);
static {
[Link]("CCCheck");
}
}
CCCheck.c
#include "CCCheck.h"
#include <string.h>
#include "LegacyCCCheck.h"
return valid;
}
jclass jc = (*env)->GetObjectClass(env,obj);
jclass jc = (*env)->FindClass(env,"MyJavaClass");
¾ Obtain an objects method ID using the class, method name, and signature:
jobject jo = (*env)->NewGlobalRef(env,obj);
[Link]
public class StockBroker implements StockListener {
int brokerID;
StockMarketEngine.c
#include <jni.h>
#include "StockMarketEngine.h"
jobject listener[10];
int lCount = 0;
js = (*env)->NewStringUTF(env, stockSymbol[y]);
jc = (*env)->GetObjectClass(env,listener[y]);
mid = (* env)->GetMethodID
(env, jc,"tradeNotification","(Ljava/lang/String;I)V");
(*env)->CallVoidMethod(env,listener[y],mid,js,x*100);
(*env)->ReleaseStringUTFChars(env,js,stockSymbol[y]);
}
}
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 331
Advanced Java Programming
JNI Signatures
¾ The first part of the parameter signature ( ) is the parameter list, followed
by the return type.
Non-primitives specify the Java-qualified type using Lclass and ending with ;.
¾ This example defines an entry that receives a String object and returns a
String result.
(Ljava/lang/String;)Ljava/lang/String;
You can use javap to determine the method signature of a Java method.
javap -s CCCheck
Labs
We have an emergency help notification system installed that pages a System Administrator
when attention is needed. It needs a message and an ID (pager number as a character string). The
files LegacyPager.c and LegacyPager.h contain the pageThem() function. Create and test a
wrapper function to invoke the pager mechanism.
(Solution: [Link], [Link], Pager.c)
Add a who() method to the Pager class to return your name as a character string when who()
receives an ID. To return a string, you will need to create a String object to return using the
followingline:
Objectives
Understand the need for Design
Patterns.
¾ They capture the expertise of many developers and make it available to all.
While patterns are published in styles that vary, all include the following
important features:
Some Background
The pattern concept was co-opted from the architect Christopher Alexander. While his writings (A Pattern
Language: Towns, Buildings, Construction and The Timeless Way of Building, 1977) described
the patterns used in buildings, the central idea of reusing solutions to common problems can be applied in
many areas.
Ward Cunningham and Kent Beck published a paper entitled "Using Pattern Languages for Object-
Oriented Programs" at the OOPSLA-87 conference, in which they described how patterns could be
applied to user-interface design.
Design Patterns, by Gamma, Helm, Vlissides, and Johnson (Gang of Four or GoF) was published in
1995 and has become the top-selling computer book of all time. The 23 patterns found in that book are still
the cornerstone of the most recent design pattern books.
Creational
Structural
Behavioral
¾ Client code only needs to know about the exposed interfaces or abstract
base classes.
Creational patterns come in two flavors: class creational and object creational.
Client Creator
ConcreteCreator1 ConcreteCreator2
«interface»
Client
Creator
ConcreteCreator1 ConcreteCreator2
Singleton — Introduction
The singleton pattern ensures that a class has only one instance.
¾ In Java, the best solution is to make the class responsible for creating this
single instance and controlling access to it.
Singleton — Implementation
The key to this pattern is encapsulating the object creation process so that the
class itself has full control.
¾ Make access to that constructor available only within the class by marking
it as private.
¾ Provide a static method that client code can use to access the singleton
instead of using a constructor.
[Link]
import [Link];
import [Link];
private MyConnection() {
try {
String driverClass = "[Link]";
String url = "jdbc:derby://localhost:1527/j2se";
String username = null;
String password = null;
[Link](driverClass);
connection =
[Link](url, username, password);
}
catch (Exception e) {
[Link](e);
}
}
public static synchronized MyConnection getInstance() {
if (theInstance == null) {
theInstance = new MyConnection();
}
return theInstance;
}
public synchronized Connection getConnection() {
return connection;
}
}
A counter
A database connection
An application-wide log file
A print spooler
A window manager
A class that controls access to a JNDI server
Java uses a singleton to represent the environment in which an application is running. To access this singleton
you call the static getRuntime() method defined within the Runtime class. This class uses an older naming
convention for the static singleton instance method: getClassname(). The method name getInstance is
more common today.
Several classes in the [Link] and [Link] packages also use the singleton pattern.
Use the factory method pattern when you need to separate the object creation
logic from the rest of the application.
The factory method pattern provides flexibility over instantiating objects directly.
¾ The product hierarchy and the creator hierarchy could have matching
subclasses.
factoryMethod()
«create»
ConcreteProduct ConcreteCreator
factoryMethod()
[Link]
public interface Product {
public void whatAmI();
}
[Link]
public class MyProduct implements Product {
public void whatAmI() {
[Link]("I'm a MyProduct");
}
}
[Link]
public class OtherProduct implements Product {
public void whatAmI() {
[Link]("I'm an OtherProduct");
}
}
¾ The second style provides a default factory method in the base class.
[Link]
...
public class DynamicFactory {
private static final String DEFAULT = "MyProduct";
Factory method.
public Product createProduct() {
String product = getProductName();
Product p = null;
try {
Class clazz = [Link](product);
p = (Product) [Link]();
}
...
return p;
}
private String getProductName() {
Properties p = new Properties();
String productToCreate = DEFAULT;
try {
FileInputStream inputStream = new Get product name
FileInputStream("[Link]"); from .properties file.
if (inputStream != null) {
[Link](inputStream);
productToCreate = [Link]("product");
}
}
catch (IOException ignore) { }
return productToCreate;
}
public static void main(String[] args) {
DynamicFactory df = new DynamicFactory();
Product p = [Link]();
...
}
}
[Link]
product=OtherProduct
Try It:
Compile [Link] as well as [Link], [Link] and [Link]. Run
DynamicFactory. Delete [Link] and run it again.
Anytime a system can be made more flexible by separating object creation from
object use, think of using a factory.
If new subclasses within a hierarchy are expected to be added in the future, the
use of a factory method can insulate the rest of the application from this churn.
¾ The client application will not need to be changed if the impact of the
addition of a new subclass can be isolated to the factory method.
¾ This also applies across all three pattern types (Creational, Structural, and
Behavioral).
EJBHome interfaces contain create() methods, which are factory methods used to create Enterprise
JavaBeans.
Builder — Introduction
If you need to create a variety of complex objects, the builder pattern can
separate the construction process from the rest of your code.
The client application communicates with the director and a concrete builder.
¾ The builder is given to the director, which uses the builder within its
internal algorithm.
¾ The director has the content for the complex object, or knows how to get
it.
¾ Finally, the client asks the director for the complex object.
c: Client Builder
getInstance(param) new
cb: ConcreteBuilder
cb
new(cb)
d: Director
build()
buildPartA(x)
buildPartB(y)
buildPartC(z)
getProduct()
complexProduct
complexProduct
Builder — Implementation
¾ The client can ask the builder base class for a concrete builder based on
an input parameter.
¾ The client could ask the director for the object explicitly.
¾ The construction method could return the object as the return argument.
Director Builder
«create»
-builder Client -file:File
+Director(Builder) +getInstance(format)
+construct():File +buildPartA()
+buildPartB()
«interface» +buildPartC()
File +getFile()
«create»
When you have an algorithm that controls the object creation process you
should investigate the possible application of the builder pattern.
There should be at least two complex product types or the strong possibility
that new types will be added.
¾ The different complex product types may have a common base class or
may be so different that it does not make sense to factor out a superclass.
The builder pattern is used by the [Link] package in the creation of certificate chains.
Labs
Run the HumanResourcesApp that is located in the chapter directory. It is designed to handle
incoming requests via separate threads (like a servlet) and create a unique employee id for each
request. It does not work correctly. Fix it by applying the appropriate pattern.
(Solutions: [Link], [Link])
Modify your code from to allow your program to create ids that start from zero (like in
) or start from a number that is read from a property file. Hint: Use the factory method
pattern to use one of two singletons based on the existence of the property file.
(Solutions: [Link], [Link], [Link],
[Link], [Link], [Link], [Link])
Run BuilderWannabe from the chapter directory and investigate the code. Refactor this class
to a pattern. emember that refactoring is taking code that works and changing it so that it is
better organized with respect to readability and maintenence without changing the behavior.
(Solutions: [Link], [Link], [Link], [Link],
[Link], [Link], [Link], [Link])
Objectives
Describe the purpose of Structural
Patterns.
Façade — Introduction
¾ Within each subsystem, you define public classes with public methods
that are accessible to other subsystems.
The façade pattern allows you to create a single, simple interface to an entire
subsystem.
¾ Your clients can use the façade if they want an easy-to-use, unified way of
accessing the most common functionality of the subsystem.
¾ You can still allow for bypassing the façade for those clients that would
like to continue communicating directly with the underlying classes.
Client
Façade
subsystem classes
Façade — Implementation
¾ Within that class, define public methods for the functionality that you
intend to expose.
Translate calls that have been exposed in the simple façade interface
to the more complex signatures of the underlying methods.
¾ Any methods and classes that should not be available to external clients
should use package or private access control rather than public.
If you choose to allow expert users to bypass the façade, then mark
those classes and methods as public, as well.
You should use the singleton pattern to handle object creation, because you will
typically only need one façade instance per subsystem.
[Link]
import [Link].*;
try {
File file = new File(filename);
out = new PrintWriter(new BufferedWriter(
new FileWriter(file, append))); This façade simplifies
[Link](text); the process of writing to
} a text or binary file.
catch (IOException e) {
log(e);
throw e;
}
finally {
[Link]();
}
}
public void writeToBinaryFile(String filename, byte[] bytes)
throws IOException {
try {
File file = new File(filename);
out = new BufferedOutputStream(
new FileOutputStream(file, append));
[Link](bytes);
}
catch (IOException e) {
log(e);
throw e;
}
finally {
...
}
}
...
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 369
Advanced Java Programming
Use the façade pattern to decouple the class implementations from their external
clients.
¾ Changes to the underlying code will not impact external clients that use the
façade.
[Link]
public class FacadeClient {
public static void main(String[] args) {
IOFacade facade = [Link]();
String text = "To be, or not to be: that is the question";
byte[] bytes = {70, 65, 67, 65, 68, 69, 1, 1, 1};
This client uses the façade to simplify writing to a file.
try {
[Link]("[Link]", text);
[Link]("[Link]", bytes);
}
catch (Exception e) {
[Link](e);
}
}
}
[Link]
public class AdvancedClient {
public static void main(String[] args) {
String s = "To sleep: perchance to dream: ay, there's the rub;\n";
insertTextInFile("[Link]", s, 395);
}
This client bypasses the
private static void insertTextInFile(String filename, façade because it needs
String textToInsert, int position) { advanced functionality that
the façade does not expose.
RandomAccessFile rac = null;
try {
rac = new RandomAccessFile(new File(filename), "rw");
[Link](position);
[Link](savedText);
[Link](position);
[Link](textToInsert);
[Link](new String(savedText));
...
}
Adapter — Introduction
When incorporating an existing class into an application, you must decide how
to merge the existing class' interface with the interface that you would have used
if you were writing the code from scratch.
¾ You may be tempted to modify your design to embrace the existing class'
interface.
¾ If you have access to the source code, you could modify the existing class
to more closely match the new design.
The existing class may have legacy clients that would be affected.
¾ You can use the adapter pattern to leverage a design that has been used
thousands of times to address this exact dilemma.
The adapter pattern allows you to convert a class' interface into an interface that
is more compatible with your design.
¾ With the adapter in place, neither the client class nor the existing class
would need to be modified.
¾ The adapter also serves as a wrapper; it allows you to swap the adaptee
without affecting the client.
There are two types of adapters: object adapters and class adapters.
With object adapters, an adaptee instance is stored within the adapter. Whenever a method is invoked on
the adapter, it delegates the request to the adaptee. The client typically references a parent interface (or
abstract class) that the adapter implements (or extends) to decouple the client from the adapter and allow
for polymorphism.
With a class adapter, the adapter inherits from both the adaptee and the target that the client
references. Any calls to the adapter method are forwarded directly to the corresponding method within the
adaptee.
In the diagram below, the Target represents the interface that the Client interacts with, but the Adapter
relies on the Adaptee to provide the actual implementation.
«interface»
Client Target
request()
Adapter Adaptee
request() specificRequest()
Adapter — Implementation
Implementing the object adapter pattern in Java involves two classes and one
interface.
¾ Create an interface that defines the method(s) that the client expects to
call.
Your adapter can also add additional methods that aren't available
in the adaptee.
AdapterClient «interface»
[Link]
main()
add()
addAll()
clear()
contains()
containsAll()
isEmpty()
iterator()
...
GenericArrayAdapter GenericArray
adaptee:GenericArray addElement()
add() indexOfObject()
addAll() getArray()
clear() getElement()
contains() getLength()
containsAll() getSize()
isEmpty() removeElement()
iterator() removeAllElement()
...
[Link]
...
public class GenericArrayAdapter<E> implements Collection<E> {
private GenericArray<E> adaptee;
public GenericArrayAdapter() {
adaptee = new GenericArray<E>();
}
public GenericArrayAdapter(int initialSize) {
adaptee = new GenericArray<E>(initialSize);
}
Use the adapter pattern any time you want to convert from one interface to
another.
Use the object adapter pattern if you would like to dynamically substitute the
adaptee.
¾ The adaptee reference that is contained by the adapter can actually refer to
any children of the adaptee, as well.
The adapter pattern can be confused with the façade pattern, because both
provide wrapping behavior, but the intent of each pattern is different:
[Link]
public class GenericArray<T> {
private T[] array;
private int currentIndex = 0;
The GenericArray is our
private int initialSize = 0;
"legacy" class that serves as
the adaptee. It does not
public GenericArray() { implement Collection.
this(1000);
}
public GenericArray(int size) {
initialSize = size;
array = getNewArray(initialSize);
}
[Link]
...
public class AdapterClient { The adapter
makes our
public static void main(String[] args) {
GenericArray
Collection<String> c1 = new ArrayList<String>(); compatible with
populateCollection(c1); Collection.
printCollection(c1);
Collection<String> c2 = new GenericArrayAdapter<String>();
populateCollection(c2);
printCollection(c2);
}
private static void populateCollection(Collection<String> c) {
[Link]("Class " + [Link]().toString());
[Link]("HashCode " + [Link]());
}
private static void printCollection(Collection<String> c) {
Iterator it = [Link]();
while ([Link]()) {
[Link]([Link]());
}
}
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 377
Advanced Java Programming
Composite — Introduction
Clients interacting with the tree structure would benefit if they could treat branch
and leaf nodes identically.
Any additional leaf or composites added to the tree will not affect
the client code.
Your client code should not know or care if it is dealing with a leaf
or a composite at any time.
The class diagram below illustrates the fact that the client always interacts with the base Component
class.
CompositeClient Component
main() operation()
add(Component)
remove(Component)
getChild(int)
Leaf Composite
operation() operation()
add(Component)
remove(Component)
getChild(int)
This object diagram shows how the composite pattern manifests itself at runtime:
:Client :Composite
Composite — Implementation
You should implement the component as an abstract class where each method
defines default behavior.
¾ Also override any other methods to recursively call the same methods on
the stored child nodes.
You can define an extra field and methods within the component to manage a
reference to its parent.
CompositeClient ArithmeticComponent
main() evaluate()
add(ArithmeticComponent)
remove(ArithmeticComponent)
getChild(int)
Operand Operator
op:int children:ArrayList
operator:String
evaluate()
evaluate()
add(Component)
remove(Component)
getChild(int)
[Link]
public abstract class ArithmeticComponent { Each method in the component
public void add(ArithmeticComponent component) { class throws an
UnsupportedOperationException.
throw new UnsupportedOperationException();
}
public void remove(ArithmeticComponent component) {
throw new UnsupportedOperationException();
}
public ArithmeticComponent getChild(int i) {
throw new UnsupportedOperationException();
}
public int evaluate() {
throw new UnsupportedOperationException();
}
}
Note:
The methods in the component class deal with maintaining child nodes and exposing the logic that each
leaf defines. These two distinct responsibilities make the component class less cohesive than most
designers would like. In fact, defining methods in a parent class that are not applicable to a child class is also
typically considered bad design. The composite pattern accepts this practice in the name of transparency.
Clients work with leaves and composites without needing to understand which type they are working with at
the expense of safety. A client may try to add a child to a leaf and get a runtime exception.
Use the composite pattern whenever you need to define a part-whole tree
structure where clients access the leaves and branches of the tree in the same
way.
¾ The parent component class defines methods that are visible to clients and
inherited by child leafs and composites.
¾ AWT uses the composite pattern to model the user interface component
hierarchy.
Nodes such as Text and Attr serve as leaf nodes, while Elements
and Documents are composites.
¾ The Java Naming and Directory Interface (JNDI) API defined in the
[Link] package is another example of the composite pattern.
[Link]
public class Operand extends ArithmeticComponent {
private int op; This is a
... leaf class.
public int evaluate() {
return op;
}
}
[Link] This is a
... composite class.
public class Operator extends ArithmeticComponent {
private ArrayList<ArithmeticComponent> children;
private String operator;
if (firstChild == true) {
result = [Link]();
firstChild = false; Composites typically
continue; use recursion.
}
if ([Link]("+"))
result += [Link]();
...
}
return result;
}
}
Try It:
Compile and run [Link] to test out the composite design pattern implementation.
Labs
Modify [Link] to write out an object using object serialization in addition to writing
text and binary files.
(Solution: [Link])
Add print functionality to the composite example that was covered in the chapter so that the
actual expression to evaluate can be displayed to the screen.
(Solutions: [Link], [Link], [Link])
Objectives
Describe the purpose of Behavioral
Patterns.
¾ They are concerned with how objects communicate with each other and
encapsulate flow control.
¾ As their name implies, they deal with the behavior of your system, not the
structure or creation.
Just like creational and structural patterns, behavioral patterns come in two
varieties: class behavioral and object behavioral.
Template — Introduction
Most algorithms are made up of a series of individual steps, where each step
defines a portion of the algorithm.
When you have multiple similar algorithms that contain the same series of steps
in the same order, but one or more of the individual steps has to be implemented
differently, you have several implementation choices:
¾ You can substitute a new implementation for some or all of the superclass
methods by overriding them in a subclass.
Page 390 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 18 Java Design Patterns — Behavioral Patterns
When using this pattern, a client will call the template method that is inherited from the superclass. The
template method will invoke methods in the superclass and subclass as necessary. The subclass methods do
not call the superclass methods unless they've been called first. Even then, the call would be through the
implicit super reference. This model is often referred to as the Hollywood principle: "Don't call us, we'll
call you."
The diagram below uses the term "primitive method" to identify those methods which make up the
individual steps of the algorithm and whose implementation is deferred to a subclass.
AbstractClass
ConcreteClass ConcreteClass2
primitiveMethod1() primitiveMethod1()
primitiveMethod2() primitiveMethod2()
Template — Implementation
To implement this pattern, you should declare an abstract class with methods
for each step in the algorithm.
You should write the template method as a public, final member of the
superclass.
¾ Your template method will call the algorithm methods in the appropriate
order.
Create one or more subclasses that override any abstract methods, providing
concrete implementations.
[Link]
import [Link];
import [Link];
import [Link]; template method
Use the template method pattern when you want to define the constant parts of
an algorithm once in a superclass and allow the parts that vary to be defined by
subclasses.
¾ The parts that vary are embedded in primitive methods and hooks.
This pattern makes sense when you want control of the order of an algorithm,
providing opportunities for overriding certain parts.
¾ You may find situations where many classes have the same behavior.
¾ You override the init(), start(), and stop() methods to provide the applet's
implementation.
¾ The template method is defined within Applet itself and guarantees that it
will call init() first, then start(), then stop().
[Link]
public class TemplateClient {
public static void main(String[] args) {
String driverClass = "[Link]";
String url = "jdbc:derby://localhost:1527/j2se";
String username = null;
String password = null;
DatabaseAccessTemplate db = new DML();
String sql = "UPDATE Employee SET lastname='Smith' WHERE id=9883";
[Link](driverClass, url, sql, username, password);
}
}
State — Introduction
¾ Designers model the state that affects an object's behavior with a state
machine.
power button press
key press
Active
Standby
Hibernate
Shutdown
standby
hibernate
shutdown
You may choose to implement your state machine with a series of conditional
statements.
¾ When you add an additional state or transition, the change will propagate
throughout each of the conditionals.
The state pattern defines a mechanism that objects use to change their behavior
based on changes to the state of the system.
¾ The object will refer to a different state object each time a transition
occurs.
The state object will embed the logic applicable to that state.
Context «interface»
State
state:State
request() handle()
State — Implementation
At the core of the state pattern implementation is an interface where you define
the common methods to all of the states.
For each state you plan to support, create a concrete class that implements the
interface.
Finally, create a "context" class whose state you are trying to abstract.
You have two choices for who should create the state objects:
¾ Alternatively, you could store all of the state instances within the context
class.
¾ The context class can handle this responsibility, provided that there are a
fixed number of transitions.
¾ The state classes themselves could know which state comes next, thereby
adding coupling between the individual states.
You will have to add a method to the context class that the state can
call to change states.
Page 398 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 18 Java Design Patterns — Behavioral Patterns
Computer «interface»
State
currentState:State
hibernate() hibernate()
keyPress() keyPress()
powerOn() powerOn()
shutdown() shutdown()
standBy() standBy()
setState(State)
...
[Link]
public class Computer {
State activeState = new ActiveState(this);
State hibernateState = new HibernateState(this);
State shutdownState = new ShutdownState(this); We will store all of
State standByState = new StandByState(this); our state instances in
State currentState; the context object.
public Computer() {
currentState = shutdownState; The initial state of the
} system is set to shutdown.
public void hibernate() {
[Link]();
}
public void keyPress() {
[Link]();
}
public void powerOn() {
[Link]();
}
...
public void setState(State state) {
currentState = state;
}
...
}
Use the state pattern when designing a system with objects whose behavior
changes at runtime based on their current state.
Use the state pattern when you are refactoring existing code that contains
conditional statements that depend on the state of an object.
¾ The state pattern isolates changes to the state objects themselves, rather
than spread the change across conditional statements.
[Link]
public interface State {
public void hibernate();
public void keyPress();
public void powerOn();
public void shutdown();
public void standBy();
}
[Link]
public class ShutdownState implements State {
private Computer computer;
Try It:
Compile and run [Link] to see each state and transition exercised.
Observer — Introduction
Use the observer pattern to define a relationship between an object that contains
state, and one or more objects that need to be notified when that state changes.
¾ The object that contains the state of interest is called the subject (or
observable).
Each of your observers is responsible for updating itself each time a notification
occurs.
¾ This ensures that each observer remains consistent with the underlying
subject.
Adding additional observers is easy because the subject maintains a list that can
be dynamically added to and removed from.
¾ Each observer instance you write will implement a parent interface that
defines the method that is called by the subject when a notification occurs.
You may also have heard of this pattern referred to as the publish-subscribe
pattern.
«interface» «interface»
Subject Observer
addObserver() update()
removeObserver()
notify()
ConcreteSubject
subject ConcreteObserver
listOfObservers
state
update()
addObserver()
removeObserver()
notifyObservers()
getState()
setState()
public void notify() {
Iterator<Observer> it = [Link]();
while ([Link]()) {
Observer obs = [Link]();
[Link]();
}
}
Observer — Implementation
Implementing the observer pattern involves creating two interfaces and at least
two concrete classes.
Write another interface that specifies the update() method for the observer(s).
You will build one concrete class that implements the subject's interface.
¾ Register the observer with the subject by storing it into a Collection when
the addObserver() method is called, using the observer interface as the
datatype for the Collection.
¾ Your notify() method should iterate through all registered observers and
call the update() method on them.
Alternatively, your client can call the notify() method when it has
completed all changes to the subject.
Write at least one concrete observer class that implements the update() method
from your observer interface.
¾ Pass a reference to the subject into the constructor and register the
observer with the subject by calling the subject's addObserver() method.
¾ Within the update() method you should query the subject for new state
information.
Page 404 Rev 5.1.4 © 2011 ITCourseware, LLC
Chapter 18 Java Design Patterns — Behavioral Patterns
«interface» «interface»
Subject Observer
addObserver() update()
removeObserver()
notify()
BankAccount
subject CurrentBalancePanel
listOfObservers:List
balance:double update()
addObserver() ...
removeObserver()
notifyObservers() subject
getBalance() StatusPanel
setBalance()
update()
...
[Link]
public interface Subject {
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
[Link]
public interface Observer {
public void update();
}
Note:
If you find that introducing this pattern has introduced too many calls to the update() method, then you may
need to modify the pattern. The pattern as described uses a "pull" method to update the observer. The
observer calls getXXX() methods on the subject to determine what state has changed; the subject is
unaware of what data the observer is interested in.
An alternate design would use a "push" model instead. The state that has changed could be passed directly
to the update() method. The drawback to this model is that the coupling of the system has increased; the
subject knows what the observers needs.
You should use the observer pattern when you have, or anticipate having, two
or more different views on underlying data.
The subject only knows about the parent interface that all observers
implement.
¾ You can still get the loose coupling benefit of this pattern even if you only
have one observer.
You should use the observer pattern when you want to be able to add new
observers without changing the subject code.
[Link]
...
public class CurrentBalancePanel extends JPanel implements Observer {
private Subject subject;
private JLabel balanceLabel;
[Link]
...
public void update() {
double currentBalance = ((BankAccount)subject).getBalance();
if (currentBalance < 0) {
[Link]([Link]());
[Link]([Link]());
[Link]([Link]);
}
...
}
...
Try It:
Compile and run [Link]. Press the deposit and withdraw buttons to change the account balance.
Watch the display to see the StatusPanel and CurrentBalancePanel automatically update themselves
whenever the balance changes.
Note:
Java has built-in support for the observer pattern within the [Link] package as defined by the
Observable class and Observer interface.
Labs
Modify the template method example that was discussed in the chapter. Create a new class called
[Link] that subclasses [Link]. Your new class should execute a
SQL SELECT statement and display the results to standard out. Test your program by listing the
contents of the Employee table.
(Solutions: [Link], [Link])
Modify the state example that was discussed in the chapter to reflect the state diagram below. Test
it out by modifying the existing client.
(Solutions: [Link], [Link], [Link], [Link],
[Link], [Link])
Using the observer example that was covered in the chapter, add another observer that acts as
a logger. It should print the current balance to standard out every time it's notified of a change.
(Solution: [Link], [Link])
Objectives
Most methods defined under [Link] throw SQLException; assume that your
JDBC code needs to either catch or declare SQLException.
Database programmers rely on a simple mechanism for obtaining error information from the database
engine: after every database operation, the DBMS sets an error value that the programmer can check. An
error is typically identified by a numeric error code (SQLCODE) and a brief error message. By convention,
DBMS error codes are negative numbers; a SQLCODE value of 0 means no error has occured; and a
special value, typically (but not always) +100, means there was no error, but no data was found to satisfy
the operation.
SQLCODE values are vendor-specific, however, so programmers must learn the specific code values for
each product they program against. To relieve this situation, standards organizations introduced the
SQLSTATE, a standardized, 5-character string encoding the error condition.
Some DBMSs now implement SQLSTATE, though most still also return their own, vendor-specific,
SQLCODE values and messages.
With JDBC, when you call certain methods, Java automatically checks the SQLSTATE/SQLCODE and
throws a SQLException when there is an error.
[Link]
...
public class SQLUtils {
public static String formatSQLException(SQLException e) {
StringBuilder msg = new StringBuilder("");
if (e != null) {
[Link](" SQLState: " + [Link]() + "\n");
[Link](" Code: " + [Link]() + "\n");
[Link](" Message: " + [Link]() + "\n\n");
}
return [Link]();
}
public static String formatSQLExceptions(SQLException e) {
String msg = "";
while (e != null) {
msg += formatSQLException(e); Call this method from a
e = [Link](); catch handler to print nice
} error messages.
return msg;
}
public static void printSQLErrors(SQLException e) {
if (e != null) {
[Link]("SQL Error:\n" + formatSQLExceptions(e));
}
}
...
¾ Use the getWarnings() method in each of these classes to get the first
SQLWarning.
The SQLWarning chain of a Statement object is cleared each time one of its
execute methods is called; a ResultSet's SQLWarnings are cleared when each
row is read.
Example:
A method for checking and printing SQLWarning information:
{
...
Connection conn;
Statement stmt;
conn = [Link](connectURL,
username, password );
stmt = [Link]();
printSQLWarnings([Link]());
...
}
[Link]
...
public class SQLUtils {
...
public static boolean printSQLWarnings(SQLWarning w)
throws SQLException {
if (w != null) {
[Link]("SQL Warning:");
while (w != null) {
[Link](formatSQLException(w));
w = [Link]();
}
return true;
}
else {
return false;
}
}
...
}
Note:
SQLWarnings may not be generated by your driver. Check your driver documentation.
JDBC Types
JDBC defines datatypes to separate the Java developer from the database
implementation.
When you use a getXXX() method on a ResultSet, the XXX refers to the Java
type that is returned.
int id = [Link](1);
String lastName = [Link](2);
¾ There is a suggested getXXX() method for each JDBC datatype, but there
is usually more than one choice.
¾ Look in the mapping table to see which JDBC types are supported by
which getXXX() methods.
The [Link] class defines a static final int for each JDBC datatype.
These constants are the way that JDBC references the type.
Mapping Table
LONGVARBINARY
LONGVARCHAR
JAVA OBJECT
VARBINARY
TIMESTAMP
SMALLINT
VARCHAR
NUMERIC
DECIMAL
INTEGER
TINYINT
DOUBLE
STRUCT
BINARY
BIGINT
ARRAY
FLOAT
CHAR
CLOB
BLOB
REAL
DATE
TIME
REF
BIT
getByte() 8 9 9 9 9 9 9 9 9 9 9 9 9
getShort() 9 8 9 9 9 9 9 9 9 9 9 9 9
getInt() 9 9 8 9 9 9 9 9 9 9 9 9 9
getLong() 9 9 9 8 9 9 9 9 9 9 9 9 9
getFloat() 9 9 9 9 8 9 9 9 9 9 9 9 9
getDouble() 9 9 9 9 9 8 8 9 9 9 9 9 9
getBigDecimal() 9 9 9 9 9 9 9 8 8 9 9 9 9
getBoolean() 9 9 9 9 9 9 9 9 9 8 9 9 9
getString() 9 9 9 9 9 9 9 9 9 9 8 8 9 9 9 9 9 9 9
getBytes() 8 8 9
getDate() 9 9 9 8 9
getTime() 9 9 9 8 9
getTimestamp() 9 9 9 9 9 8
getAsciiStream() 9 9 8 9 9 9
getBinaryStream() 9 9 8
getCharacterStream() 9 9 8 9 9 9
getClob() 8
getBlob() 8
getArray() 8
getRef() 8
getObject() 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 8 8
8 — Preferred 9 — Optional (can implicitly convert)
¾ The database parses and compiles the query string each time you call
executeQuery().
[Link]
...
public class EmpSals {
String sqltxt;
sqltxt = "SELECT firstname, lastname, salary, department_name" +
" FROM employee, department " +
" WHERE employee.department_code=department.department_code";
ResultSet rs = [Link](sqltxt);
[Link]();
[Link]();
}
catch (ClassNotFoundException cnfe) {
[Link]("Unable to load driver class: " + cnfe);
[Link](1);
}
catch (SQLException sqle) {
[Link]("SQL Error:");
[Link](" Code: " + [Link]());
[Link](" Message: " + [Link]());
[Link](1);
}
}
}
ResultSetMetaData
For queries built at runtime for which you need to determine the number, types,
names, etc. of columns of a ResultSet, get the ResultSet's metadata.
int getColumnCount()
String getColumnName(int column)
String getColumnLabel(int column)
int getColumnType(int column)
String getColumnTypeName(int column)
int getPrecision(int column)
int getScale(int column)
int getColumnDisplaySize(int column)
int isNullable(int column)
boolean isReadOnly(int column)
boolean isCurrency(int column)
etc...
[Link]
...
public class ExecuteFormat {
switch ([Link](col)) {
case [Link]:
case [Link]:
return 9;
case [Link]:
return 22;
case [Link]:
return 10;
case [Link]:
return 8;
default :
return [Link](col);
}
}
}
Try It:
Run [Link] to see all Department data printed out in columns with headers.
executeUpdate() returns an int, the update count, which is one of two values:
int uc;
uc = [Link]( "UPDATE employee "
+ " SET salary = salary * 1.1 "
+ "WHERE title = 'Engineer'");
The Statement interface's execute() method can execute an arbitrary SQL string that might generate
either a result set or an update count. The price of this generality is some extra coding to retrieve the actual
results. Usually, though, you will use executeQuery() for queries and executeUpdate() for DML and
DDL.
Using a PreparedStatement
If your program executes the same SQL statement repeatedly, perhaps just with
different values in certain places, you may benefit by using a
PreparedStatement.
PreparedStatement pst;
pst=[Link]("SELECT department_name"
+ " FROM department"
+ " WHERE department_code = 'RD'");
ResultSet rs = [Link]();
¾ You must supply the SQL syntax when you create the
PreparedStatement, not when you execute it.
<<interface>>
Statement
<<interface>>
PreparedStatement
Parameterized Statements
You can execute a prepared statement multiple times with different values
inserted into the statement syntax.
When creating the statement, use a question mark, ?, as a placeholder for any
value you will change later.
PreparedStatement pst;
pst = [Link]( "SELECT department_name"
+ " FROM department"
+ " WHERE department_code = ?");
You must set values for all placeholders before executing the statement.
[Link](1, "RD");
ResultSet rs = [Link]();
¾ The setXXX() methods take the index of the placeholder, from left-to-
right starting at 1, as the first argument.
This example uses a prepared statement to retrieve the department name using the department code.
[Link]
...
public class PrepParamExample {
private PreparedStatement pst;
public PrepParamExample() {
try {
[Link]("[Link]");
String url = "jdbc:derby://localhost:1527/j2se";
Connection conn = [Link](url);
[Link](getDeptName("RD"));
[Link](getDeptName("HR"));
[Link]();
[Link]();
}
catch (Exception e) {
[Link]();
}
}
Try It:
Compile and run this program. The output will be the department names for the "RD" and "HR"
departments.
Stored Procedures
CallableStatement cst;
cst = [Link]("{call myproc()}");
[Link](1, "SM")
[Link](2, 9144);
[Link]();
<<interface>>
Statement
<<interface>>
PreparedStatement
<<interface>>
CallableStatement
Transaction Management
In most cases, autocommit is not desirable — you don't have the ability to check errors and warnings
and program appropriately, or to treat multiple individual statements as parts of a single transaction, or
to set transaction savepoints (if your DBMS supports them).
¾ Some DBMSs, notably Oracle, automatically commit the current transaction any time you
execute a DDL statement.
¾ Committing the current transaction frees the update locks associated with an updatable cursor,
thus forcing the cursor closed.
Isolation Level
Your transaction isolation level describes how your session behaves in regard to ongoing,
uncommitted transactions performed by other users.
Note:
Your DBMS may not provide all isolation levels.
© 2011 ITCourseware, LLC Rev 5.1.4 Page 431
Advanced Java Programming
Labs
Write a program to print a listing of all employee names and salaries, and the total of all salaries at
the end.
(Solution: [Link])
Modify the salary report program from [Link] so that after the list of all employee
names, it prints a list of the department names with salary totals for each. (Hint: Use a HashMap to
accumulate the salary total for each department.)
(Solution: [Link])
(Optional) Modify the salary report program to use rPadTrunc(), found in the provided
[Link], to format the output.
(Solutions: [Link], [Link])
Write a program that uses a PreparedStatement to retrieve the id, name, and title of all employees
of a department. The statement should take the department code as its only parameter. Prompt the
user for a department code and use the response to run the query and print the results.
(Solution: [Link])
Write a program that prompts the user to enter a SQL statement (using [Link] or a simple GUI,
as you prefer). The program should then determine if the statement is a query or not, and execute it
using the appropriate method. If it is a query, just print out the number of rows found. If it is a DML
or other statement, print the number of rows affected.
(Solution: [Link])
Appendix B - Eclipse
Objectives
Install and configure Eclipse.
Introduction to Eclipse
IBM started the Eclipse project in April of 1999 with the goal of "eclipsing" the
dominance of Microsoft Visual Studio within the integrated development
environment (IDE) space.
¾ IBM donated the initial Eclipse code base to the open source community
in November of 2001 — just one month after version 1.0 was released.
Eclipse has become much more than a pure Java, open source IDE; it is a
framework on which companies can develop their own tools.
¾ Users can combine tools from different vendors to accomplish their goals.
¾ The Eclipse Platform is the core IDE that most people associate with the
term "Eclipse."
¾ The Java Development Tools (JDT) project provides the necessary plug-
ins to support development of Java applications.
These tools include the incremental compiler, the debugger, and the
editor.
Installing Eclipse
¾ The file you download will be a .zip file (or [Link]), rather than an install
program.
You do not need the full Java Development Kit (JDK), unless you
want to be able to step through the library source code during
debug.
¾ Eclipse will use the first Java VM it finds in your PATH when it runs.
To explicitly set the VM, pass the -vm argument to the Eclipse
executable:
The first time you run Eclipse, it will ask you to select a workspace location.
After you have selected a workspace, you will see a welcome screen that
provides links to tutorials, sample applications, overview topics, and
information on what's new in the current version of Eclipse.
¾ You can skip this introductory content by choosing the Workbench link in
the upper right corner of the screen or by clicking the x next to the word
Welcome in the upper left corner of the screen.
To get back to the welcome screen in the future, you can choose the
HelpÆWelcome menu item.
¾ The JDT Java Editor provides syntax coloring, code completion, and code
formatting, among other things.
¾ Multiple editors can be open at once and they will appear as stacked
instances with individual tabs for selection.
Views display information about an object; they typically supplement the data
that is visible in the current editor.
¾ The JDT provides a Packages View, Type Hierarchy View, and Java
Outline View.
¾ You can switch from one perspective to another to see the appropriate
combination of views and editors for your current needs by choosing
WindowÆOpen Perspective.
¾ The Window menu also allows you to save, customize, reset, and close
Perspectives.
The Java Perspective is the default perspective that you will see after
dismissing the welcome screen.
Setting up a Project
Before you can edit any Java code, you must first create a project.
¾ You can find the .java files that correspond to the code you are editing
within your workspace directory.
¾ Select Java Project from the list of choices and click Next.
¾ You can also specify which version of Java your project should be
compliant with.
Once your project has been created, you will see the project name listed in the
Package Explorer view.
¾ You can enter the name of the package to which your new class should
belong.
Leave the Package text field blank to place the class in the default
package.
¾ You must specify a name for the new class in the Name text field.
¾ The Superclass and Interfaces text boxes allow you to identify the
extends and implements clauses for your new class.
¾ Eclipse can also automatically generate the main() method, any parent
abstract methods, constructors, and javadoc comments for you.
The new class will automatically open up in a Java editor where you can add
your code to the class.
Every time you save the file, Eclipse will compile your class for you.
¾ Any compiler errors are visible in the Problems view at the bottom of the
screen.
¾ The results will be displayed in the Console view at the bottom of the
screen.
If you would like to run your program with custom options, choose RunÆRun
Configurations.
¾ The next dialog allows you to pass in arguments and customize the
environment before you invoke the program.
Standard out
and standard
err are shown in
the console.
¾ You will be notified that the perspective will change to the debug
perspective before the debugger starts.
Within the debug perspective, you are presented with multiple views.
¾ The Debug view contains toolbar buttons that allow you to step through
the program as it runs.
It also displays the current location within the method call stack.
¾ The Variables view shows you the current value of the variables in your
program.
You can also hover your mouse over any variable reference in the
Editor to see its current value.
¾ The Console view displays the current results that have been written to
standard out.
¾ The Outline view displays the structure of your class and highlights the
current method you have stepped into.
After you have completed debugging your program, switch back to the Java
Perspective by clicking WindowÆOpen PerspectiveÆJava or you can click
the Java button in the upper right corner of the screen.
Page 450 Rev 5.1.4 © 2011 ITCourseware, LLC
Appendix B Eclipse
Note:
If you have installed a JDK rather than a JRE, Eclipse can step into the source code for the Java API
libraries.
¾ Use this key sequence to have Eclipse generate a list of possible matches
for the current entry in the code.
¾ Click on a class that has been flagged as "not resolved" and use this key
sequence to have Eclipse automatically discover and add an import for
the class.
¾ This will take the current view or editor you are working in and maximize
it; use the sequence again to go back to the original size.
¾ This key sequence will comment out the current line or lines with a //
comment; press it again to uncomment.
¾ Use this key sequence to comment out the current block of lines with a
block comment /* */; use a backslash to remove a block comment.
Eclipse is loaded with features that tend to be hidden deep beneath menu options. To see a list of all of the
shortcut keys, use <Ctrl><Shift>L.
Another handy set of shortcuts to remember can be found under the Source menu. For example, the
SourceÆGenerate Getters and Setters menu item will create gets and sets for any of the fields in your
class.
¾ These key sequences will move the current line(s) down or up.
¾ Use the Open Type dialog to quickly jump to a class or interface in your
project or in the Java libraries
¾ For example, type Str for all classes that start with these three characters
(String, StringBuffer, etc.)
¾ Use the Open Resource dialog to find any kind of file in your project.
¾ Any time you have clicked on a method call in code, you can click F3 to
jump to the method declaration in the Java editor.
¾ Use these key sequences like back and forward buttons in a browser.
¾ Use this key sequence to launch a dialog that helps you quickly find a
method within the current file by simply typing in the first few letters of
the method you are looking for.
In addition to key sequences, Eclipse also provides templates that allow you to quickly insert common
code snippets. Type the template, followed by <Ctrl><Space> to see the template replaced with the
full syntax.
sysout — [Link]()
syserr — [Link]()
try — try/catch block
catch — catch block
main — main() method
If you are using Java code that is external to your project, then you need to tell
Eclipse where to find it.
¾ You can reference a .jar file or a directory that contains .class files.
¾ On the Libraries tab, choose either the Add External JARs button or the
Add Class Folder button to select the .jar or directory where your .class
files are located.
¾ Click the New button and choose a unique name for your variable.
¾ Use the File button to browse to a .jar file, or the Folder button to choose
a directory structure that contains the .class files, to add to the Classpath
variable
¾ You can reference the global variables from your individual projects using
ProjectÆPropertiesÆJava Build Path.
On the Libraries tab, choose the Add Variable button to select the
global Classpath variable you just created.
To load pre-existing Java files into an Eclipse project, use the Import feature by
choosing FileÆImport.
¾ Choose File System to tell Eclipse to look on your local drives for the
existing Java files.
¾ Choose the checkboxes for the packages to import into your project.
¾ Make sure the Into folder text field specifies the name of the workspace
folder within your project you wish to import into.
¾ Click Finish.
You can also copy the files into the workspace folder using your file system's
copy utilities.
On Windows systems, you can even drag the files from your file system into an
Eclipse project!
Note:
If you are creating a new project for your existing files and you do not want to copy all of the files into a
new workspace, then you can create your new Java project and set its workspace location to where your
Java source files are already located. Just choose FileÆNewÆProject, choose Java Project, and then
choose to Create project from existing source. Browse to the proper directory and you are done.
Solutions
Chapter 2
[Link]
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
[Link](" ").append(firstName);
[Link](" ").append(lastName);
[Link](" hired ").append([Link](hireDate));
return [Link]();
}
public String getName() {
return firstName + " " + lastName;
}
public String getTitle() {
return title;
}
public Date getHireDate() {
return hireDate;
}
public int getId() {
return id;
}
public int getSupervisorId() {
return supervisorId;
}
}
[Link]
import [Link];
Chapter 2 (cont'd)
[Link] (cont'd)
[Link]([Link]) &&
[Link]([Link]);
}
else
return false;
}
public String toString() {
return code + " " + name + " manager: " + manager;
}
public String getCode(){
return code;
}
public String getName(){
return name;
}
public Employee getManager(){
return manager;
}
public void setName(String nm){
name = nm;
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
try {
out = new ObjectOutputStream(new FileOutputStream("[Link]"));
}
catch (IOException e) {
[Link]("Error opening output file: "
+ [Link]());
[Link](1);
}
try {
[Link](space);
[Link]("Wrote \"" + space + "\" to file");
}
catch (IOException e) {
[Link]("Error writing to file: " + [Link]());
[Link](2);
}
finally {
try {
[Link]();
}
catch (IOException e) {
[Link](3);
}
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
Chapter 2 (cont'd)
[Link] (cont'd)
Department d = null;
try {
d = (Department) [Link]();
}
catch (ClassCastException e) {
[Link]("Error casting object to a Department: " +
[Link]());
[Link](2);
}
catch (IOException e) {
[Link]("Error reading object: " + [Link]());
[Link](3);
}
catch (ClassNotFoundException e) {
[Link]([Link]() + " class not found");
[Link](4);
}
finally {
try {
[Link]();
}
catch (IOException e) {
[Link](5);
}
}
// write to the screen
[Link](d);
}
}
[Link]
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
}
catch (IOException e) {
[Link]("Error opening input file: " +
[Link]());
[Link](1);
}
Department d = null;
try {
d = (Department) [Link]();
}
catch (ClassCastException e) {
[Link]("Error casting object to a Department: " +
[Link]());
[Link](2);
}
catch (IOException e) {
[Link]("Error reading object: " + [Link]());
[Link](3);
}
catch (ClassNotFoundException e) {
[Link]([Link]() + " class not found");
[Link](4);
}
finally {
try {
[Link]();
}
catch (IOException e) {
[Link](5);
}
}
// write to the screen
[Link](d);
}
}
[Link]
SP Space Division manager: Astronaut Neil Armstrong hired Aug 5, 1960
[Link].4
import [Link];
import [Link];
import [Link];
© 2011 ITCourseware, LLC Rev 5.1.4 Page 467
Advanced Java Programming
Chapter 2 (cont'd)
[Link].4 (cont'd)
public class Employee implements Serializable {
static final long serialVersionUID = -8853382621975997090L;
private int id;
private String firstName;
private String lastName;
private String title;
private String departmentCode;
private int supervisorId;
private Date hireDate;
private float salary;
private Date departmentStartDate;
[Link].4 (cont'd)
}
public String toString() {
SimpleDateFormat df = new SimpleDateFormat("MMM d, yyyy");
StringBuilder result = new StringBuilder();
[Link](title);
[Link](' ').append(firstName);
[Link](' ').append(lastName);
[Link](" hired ").append([Link](hireDate));
return [Link]();
}
public String getName() {
return firstName + " " + lastName;
}
public String getTitle() {
return title;
}
public Date getHireDate() {
return hireDate;
}
public int getId() {
return id;
}
public int getSupervisorId() {
return supervisorId;
}
public Date getDepartmentStartDate() {
return departmentStartDate;
}
}
[Link].5
import [Link];
import [Link];
import [Link];
Chapter 2 (cont'd)
[Link].5 (cont'd)
private float salary;
private Date departmentStartDate;
[Link].5 (cont'd)
[Link](" department start date ");
[Link]([Link](departmentStartDate));
return [Link]();
}
public String getName() {
return firstName + " " + lastName;
}
public String getTitle() {
return title;
}
public Date getHireDate() {
return hireDate;
}
public int getId() {
return id;
}
public int getSupervisorId() {
return supervisorId;
}
public Date getDepartmentStartDate() {
return departmentStartDate;
}
}
[Link]
When you run ReadDept using the new class on the old object it throws
a NullPointerException because the [Link](Date)
method receives a null parameter when we pass departmentStartDate.
A newly constructed object will never have this problem because the
constructors provide a default value for departmentStartDate. However,
when we deserialize an old version of the class in which that field is
missing, it is set to null by the default deserialization mechanism.
[Link].6
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 2 (cont'd)
[Link].6 (cont'd)
private int id;
private String firstName;
private String lastName;
private String title;
private String departmentCode;
private int supervisorId;
private Date hireDate;
private float salary;
private Date departmentStartDate;
[Link].6 (cont'd)
[Link]([Link]) &&
[Link]([Link]) &&
[Link]([Link]);
}
else return false;
}
public String toString() {
SimpleDateFormat df = new SimpleDateFormat("MMM d, yyyy");
StringBuilder result = new StringBuilder();
[Link](title);
[Link](" ").append(firstName);
[Link](" ").append(lastName);
[Link](" hired ").append([Link](hireDate));
[Link](" department start date ");
[Link]([Link](departmentStartDate));
return [Link]();
}
public String getName() {
return firstName + " " + lastName;
}
public String getTitle() {
return title;
}
public Date getHireDate() {
return hireDate;
}
public int getId() {
return id;
}
public int getSupervisorId() {
return supervisorId;
}
public Date getDepartmentStartDate() {
return departmentStartDate;
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 2 (cont'd)
[Link] (cont'd)
public class WriteDept7 {
public static void main(String args[]) throws ParseException {
SimpleDateFormat f = new SimpleDateFormat("M/d/yyyy");
Date hd = [Link]("08/05/1960");
Date dsd = [Link]("01/01/1962");
Employee armstrong = new Employee(5, "Neil", "Armstrong",
"Astronaut", "SP", 1, hd, dsd, 30000);
Department space = new Department("SP", "Space Division",
armstrong, "Edwards", "CA");
ObjectOutputStream out = null;
try {
out = new ObjectOutputStream(new FileOutputStream("[Link]"));
}
catch (IOException e) {
[Link]("Error opening output file: "
+ [Link]());
[Link](1);
}
try {
[Link](space);
[Link]("Wrote \"" + space + "\" to file");
}
catch (IOException e) {
[Link]("Error writing to file: " + [Link]());
[Link](2);
}
finally {
try {
[Link]();
}
catch (IOException e) {
[Link](3);
}
}
}
}
Chapter 3
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
Chapter 3 (cont'd)
[Link] (cont'd)
[Link](0, [Link](), fcout);
[Link]();
[Link]();
}
catch (Exception e) {
[Link]();
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
}
}
}
[Link]
The second version uses UTF-16 encoding, so when we look at the result file it looks like the
characters are spaced out.
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 4
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
return o;
}
public Vector<Method> getMethods(Object obj) {
Vector<Method> v = new Vector<Method>();
// fill in code here
return v;
}
public Object invoke(Method method, Object target, Object[] args) {
Object retValue = null;
// fill in code here
[Link] (cont'd)
return retValue;
}
public Vector<String> getClasses() {
Vector<String> v = new Vector<String>();
File curDir = new File(".");
File[] classFiles = [Link](new FileFilter() {
public boolean accept(File f) {
return [Link]().contains(".class");
}});
for (File file : classFiles) {
String name = [Link]();
name = [Link](0, [Link](".class"));
[Link](name);
}
return v;
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 4 (cont'd)
[Link] (cont'd)
return v;
}
public Object instantiate(Constructor ctor, Object[] args) {
Object o = null;
try {
o = [Link](args);
}
catch (Exception e) {
[Link]();
}
return o;
}
public Vector<Method> getMethods(Object obj) {
Vector<Method> v = new Vector<Method>();
// fill in code here
return v;
}
public Object invoke(Method method, Object target, Object[] args) {
Object retValue = null;
// fill in code here
return retValue;
}
public Vector<String> getClasses() {
Vector<String> v = new Vector<String>();
File curDir = new File(".");
File[] classFiles = [Link](new FileFilter() {
public boolean accept(File f) {
return [Link]().contains(".class");
}});
for (File file : classFiles) {
String name = [Link]();
name = [Link](0, [Link](".class"));
[Link](name);
}
return v;
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 4 (cont'd)
[Link] (cont'd)
}
public Object invoke(Method method, Object target, Object[] args) {
Object retValue = null;
// fill in code here
return retValue;
}
public Vector<String> getClasses() {
Vector<String> v = new Vector<String>();
File curDir = new File(".");
File[] classFiles = [Link](new FileFilter() {
public boolean accept(File f) {
return [Link]().contains(".class");
}});
for (File file : classFiles) {
String name = [Link]();
name = [Link](0, [Link](".class"));
[Link](name);
}
return v;
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
for (Constructor ctor : ctors) {
[Link](ctor);
}
}
catch (ClassNotFoundException cnfe) {
[Link]();
}
return v;
}
public Object instantiate(Constructor ctor, Object[] args) {
Object o = null;
try {
o = [Link](args);
}
catch (Exception e) {
[Link]();
}
return o;
}
public Vector<Method> getMethods(Object obj) {
Vector<Method> v = new Vector<Method>();
Class cls = [Link]();
Method[] methods = [Link]();
for (Method method : methods) {
[Link](method);
}
return v;
}
public Object invoke(Method method, Object target, Object[] args) {
Object retValue = null;
try {
retValue = [Link](target, args);
}
catch (Exception e) {
[Link]();
}
return retValue;
}
public Vector<String> getClasses() {
Vector<String> v = new Vector<String>();
File curDir = new File(".");
File[] classFiles = [Link](new FileFilter() {
public boolean accept(File f) {
return [Link]().contains(".class");
}});
Chapter 4 (cont'd)
[Link] (cont'd)
for (File file : classFiles) {
String name = [Link]();
name = [Link](0, [Link](".class"));
[Link](name);
}
return v;
}
}
Chapter 5
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
while ([Link]()) {
String filepath = [Link](1) + ".gif";
Blob b = [Link](2);
[Link]
import [Link];
import [Link];
© 2011 ITCourseware, LLC Rev 5.1.4 Page 485
Advanced Java Programming
Chapter 5 (cont'd)
[Link] (cont'd)
import [Link];
import [Link];
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Page 486 Rev 5.1.4 © 2011 ITCourseware, LLC
Solutions
[Link] (cont'd)
import [Link];
while ([Link]()) {
[Link]([Link]("lastname") + " " +
[Link]("hire_date") + " " +
[Link]("salary"));
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 5 (cont'd)
[Link] (cont'd)
import [Link];
while ([Link]()) {
[Link]([Link]("lastname") + " " +
[Link]("hire_date") + " " +
[Link]("salary"));
}
[Link]("employee");
[Link](conn);
[Link]();
[Link]();
}
catch (Exception e) {
[Link]();
}
}
}
Chapter 6
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 6 (cont'd)
[Link] (cont'd)
imgFile = new FileOutputStream(fileName);
imgStream = [Link]();
int len = -1;
while ((len = [Link](buffer)) != -1) {
[Link](buffer, 0, len);
}
}
catch (IOException ioex) {
[Link]("Error copying image: " + ioex);
[Link](3);
}
finally {
if (imgFile != null) {
try {
[Link]();
}
catch (IOException ignore) {}
}
if (imgStream != null) {
try {
[Link]();
}
catch (IOException ignore) {}
}
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
ServerSocket ss = null;
Socket s = null;
Page 490 Rev 5.1.4 © 2011 ITCourseware, LLC
Solutions
[Link] (cont'd)
OutputStream sockout = null;
try {
FileInputStream in = new FileInputStream(file);
data = new byte[[Link]()];
[Link](data);
[Link]();
}
catch (IOException e) {
[Link]("Error reading " + file + ": " + e);
[Link](1);
}
try {
ss = new ServerSocket(port);
while (true) {
// wait for the connection
s = [Link]();
[Link]();
}
}
catch (IOException e) {
[Link](e) ;
}
finally {
// Always be sure to close the sockets to release resources
if (ss != null) {
try {
[Link]();
}
catch (IOException ignore) {}
Chapter 6 (cont'd)
[Link] (cont'd)
}
if (s != null) {
try {
[Link]();
}
catch (IOException ignore) {}
}
}
}
}
[Link]
There's nothing very mysterious about you, except that
nobody really knows your origin, purpose, or destination.
Tact, n.:
The unsaid part of what you're thinking.
The most exciting phrase to hear in science, the one that heralds new
discoveries, is not "Eureka!" (I found it!) but "That's funny ..."
-- Isaac Asimov
Speaking of love, one problem that recurs more and more frequently these
days, in books and plays and movies, is the inability of people to communicate
with the people they love; Husbands and wives who can't communicate, children
who can't communicate with their parents, and so on. And the characters in
these books and plays and so on (and in real life, I might add) spend hours
bemoaning the fact that they can't communicate. I feel that if a person can't
communicate, the very _____ least he can do is to shut up!
-- Tom Lehrer, "That Was the Year that Was"
[Link] (cont'd)
Her days were spent in a kind of slow bustle; always busy without getting
on, always behind hand and lamenting it, without altering her ways;
wishing to be an economist, without contrivance or regularity; dissatisfied
with her servants, without skill to make them better, and whether helping, or
reprimanding, or indulging them, without any power of engaging their respect.
-- J. Austen
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
// create the connection
s = new Socket([Link](), 8000);
Chapter 6 (cont'd)
[Link] (cont'd)
}
catch (IOException ignore) {}
}
}
}
}
[Link]
public class ServerStatistics implements [Link] {
private String fileName;
private int connectionCount;
private int port;
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
ServerSocket ss = null;
Socket s = null;
OutputStream sockout = null;
try {
FileInputStream in = new FileInputStream(file);
data = new byte[[Link]()];
[Link](data);
[Link]();
}
catch (IOException e) {
[Link]("Error reading " + file + ": " + e);
[Link](1);
}
try {
ss = new ServerSocket(port);
while (true) {
// wait for the connection
s = [Link]();
[Link]();
Chapter 6 (cont'd)
[Link] (cont'd)
sockout = [Link]();
[Link]();
}
}
catch (IOException e) {
[Link](e) ;
}
finally {
// Always be sure to close the sockets to release resources
if (ss != null) {
try {
[Link]();
}
catch (IOException ignore) {}
}
if (s != null) {
try {
[Link]();
}
catch (IOException ignore) {}
}
}
}
}
class AdminThread extends Thread {
private int adminPort;
private ServerStatistics statistics;
[Link] (cont'd)
public void run() {
ServerSocket ss = null;
Socket s = null;
ObjectOutputStream sockout = null;
try {
ss = new ServerSocket(adminPort);
while (true) {
s = [Link]();
sockout = new ObjectOutputStream([Link]());
[Link](statistics);
[Link]();
}
}
catch (IOException e) {
[Link]("Error in admin thread: " + e) ;
}
finally {
// Always be sure to close the sockets to release resources
if (ss != null) {
try {
[Link]();
}
catch (IOException ignore) {}
}
if (s != null) {
try {
[Link]();
}
catch (IOException ignore) {}
}
}
}
}
Chapter 6 (cont'd)
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
try {
// Connect to the server's admin port
s = new Socket([Link](), 9000);
Chapter 7
[Link]
import [Link];
[Link]
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 7 (cont'd)
[Link] (cont'd)
public AttendantServer2(StoreServer2 store) throws RemoteException {
[Link] = store;
tf = [Link]();
[Link]([Link]());
}
return orderNum;
}
public boolean release() {
boolean success = false;
try {
success = [Link](this, false);
}
catch (NoSuchObjectException e) {
[Link](e);
}
return success;
}
}
[Link]
import [Link];
[Link] (cont'd)
Order order = new Order();
[Link]("hdw123", 5);
[Link]("app456", 2);
[Link]("per789", 3);
int orderNumber = [Link](order);
[Link]("The order number is: " + orderNumber);
[Link]();
}
catch (Exception e) {
[Link](e);
}
}
}
[Link]
import [Link];
public Order() {
orderItems = new HashMap<String, Integer>();
}
[Link]
import [Link];
import [Link];
Chapter 7 (cont'd)
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
}
catch (RemoteException e) {
[Link](e);
}
}
}
[Link]
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 7 (cont'd)
[Link] (cont'd)
String[] codes = new String[[Link]()];
return [Link](codes);
}
public Item3 getItem(String code) {
return [Link](code);
}
public int submitOrder(Order order) throws RemoteException {
int orderNum = [Link]();
[Link]
import [Link];
Item3 i = [Link]("hdw123");
[Link]("Item Code: " + [Link]() +
" Description: " + [Link]() +
" Quantity On Hand: " + [Link]());
[Link]();
}
catch (Exception e) {
[Link](e);
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
Chapter 7 (cont'd)
[Link] (cont'd)
public String getItemCode() throws RemoteException;
public String getDescription() throws RemoteException;
public int getQuantityOnHand() throws RemoteException;
}
[Link]
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 7 (cont'd)
[Link] (cont'd)
try {
StoreServer3 store = new StoreServer3();
[Link]("///Store3", store);
}
catch ([Link] e) {
[Link](e);
}
catch (RemoteException e) {
[Link](e);
}
}
}
Chapter 8
[Link]
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 8 (cont'd)
[Link] (cont'd)
[Link](n);
}
}
public static void main(String[] args) {
try {
ChatServer c = new ChatServer();
[Link]("//localhost:1099/chat", c);
[Link]("Chat bound.");
}
catch (Exception e) {
[Link](e);
}
}
}
[Link]
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
ChatIF c = (ChatIF)[Link]("//localhost:1099/chat");
[Link](this);
while (true) {
[Link]("Enter a chat message, '##' to quit: ");
String line = [Link]();
if ([Link]("##")) {
[Link](0);
}
[Link](line, alias);
}
}
catch (Exception e) {
[Link](e);
}
}
public void notify(String msg, String sender) {
[Link]("\n" + sender + ": " + msg);
}
public static void main(String[] args) {
try {
new ChatClient();
}
catch (RemoteException e) {
[Link](e);
}
}
}
ISBN_IF.java
import [Link];
import [Link];
ISBN_Impl.java
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
© 2011 ITCourseware, LLC Rev 5.1.4 Page 511
Advanced Java Programming
Chapter 8 (cont'd)
ISBN_Impl.java (cont'd)
public class ISBN_Impl extends Activatable implements ISBN_IF {
private HashMap<String, String> map = null;
[Link]
/* allow the JVM for an activation group to specify a security policy */
grant {
permission [Link] "-
[Link]=*";
};
[Link]
/* allow remote objects in the activation group to accept
* connetcionts on any non-privileged port
*/
grant {
permission [Link] "*:1024-", "accept,resolve";
};
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
ActivationDesc desc =
new ActivationDesc (groupId, "ISBN_Impl", codebase, null);
Chapter 8 (cont'd)
[Link]
0131483986, Java How to Program
1586639161, Java
0201310058, Effective Java Programming Language Guide
0321305027, The Java Developer's Guide to Eclipse
0596009208, Head First Java
0596007736, Java in a Nutshell
[Link]
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
Map<String, Float> stocks = null;
Chapter 8 (cont'd)
[Link] (cont'd)
public static void main(String[] args) {
try {
StockServer2 s = new StockServer2();
Context ctx = new InitialContext();
[Link]("StockService", s);
[Link]("Stock bound.");
for (int i = 0; i < 100; i++ ) {
[Link](1000);
[Link]("MSFT", [Link]("MSFT") * 1.03f);
[Link]("OATS", [Link]("OATS") * 1.05f);
[Link]("EBAY", [Link]("EBAY") * 1.08f);
}
[Link]("StockService");
[Link](0);
}
catch(Exception e) {
[Link](e);
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
}
}
catch(Exception e) {
[Link](e);
}
}
Chapter 9
[Link]
Your policy file should include:
[Link]
Command:
appletviewer [Link]
[Link]
Your .[Link] file should now contain the added permission:
Command:
appletviewer [Link]
Clicking the applet's Close button should now exit the appletviewer without
throwing an exception.
[Link]
When you run ShowTimeClient, TimeServer has an exception:
Exception in thread "RMI TCP Connection(2)-[Link]" [Link]
Exception: access denied ([Link] [Link]:1121 accept,resolve
)
The exception tells you TimeServer needs a SocketPermission with accept and
resolve actions.
After adding the accept permission, your .[Link] file should look something like
this:
[Link] (cont'd)
grant codeBase "[Link] {
permission [Link] "*:*", "connect, resolve, accept";
permission [Link] "C:\\advj2se\\ch09\\server\\-", "read";
permission [Link] "C:\\advj2se\\ch09\\*", "write";
permission [Link] "exitVM";
};
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 9 (cont'd)
[Link]
Your [Link] file should look like this:
Chapter 10
[Link]
Volume in drive C is ENTERPRISE
Volume Serial Number is 3926-10F1
Directory of C:\advj\ch16\META-INF
[Link]
Keystore type: jks
Keystore provider: SUN
[Link]
Volume in drive C is ENTERPRISE
Volume Serial Number is 3926-10F1
Directory of C:\advj\ch16\meta-inf
Chapter 10 (cont'd)
[Link]
import [Link].*;
import [Link];
import [Link];
import [Link];
import [Link].X509Certificate;
import [Link];
if ([Link](a)) {
[Link](a + " is a key.");
Key key = [Link](a, [Link]());
[Link]("Private Key:");
[Link](key);
}
else if ([Link](a)) {
[Link](a + " is a certificate.");
}
[Link] (cont'd)
[Link]("Not valid after: " +
[Link]());
}
}
}
catch (Exception e) {
[Link]();
}
}
}
Chapter 11
[Link]
import [Link].*;
import [Link];
import [Link];
import [Link];
import [Link];
Cipher c = [Link]("DES/ECB/PKCS5Padding");
[Link](Cipher.ENCRYPT_MODE, theKey);
String s;
while(!(s = [Link]()).equalsIgnoreCase("EOF")) {
[Link](s);
}
[Link]();
[Link]();
[Link]
import [Link].*;
import [Link];
import [Link];
import [Link];
Cipher c = [Link]("DES/ECB/PKCS5Padding");
[Link](Cipher.DECRYPT_MODE, theKey);
String s;
while((s = [Link]()) != null) {
[Link](s);
}
[Link]();
}
catch (Exception e) {
[Link]("Error : " + [Link]());
}
}
}
[Link]
import [Link].*;
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 11 (cont'd)
[Link] (cont'd)
public class PasswordDecrypt {
public static void main(String args[]) {
try {
BufferedReader br = new BufferedReader(
new InputStreamReader([Link]));
[Link]("Enter Password for Decryption: ");
String password = [Link]();
Cipher c = [Link]("PBEWithMD5AndDES");
[Link](Cipher.DECRYPT_MODE, theKey, paramSpec);
String s;
while((s = [Link]()) != null)
[Link](s);
}
catch (Exception e) {
[Link]("Error : " + [Link]());
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
public PasswordList() {
try {
buf = new BufferedReader(new InputStreamReader([Link]));
String password = promptForPassword();
createCipher(password);
passwords = getPasswordList();
int opt;
while ((opt = displayMenu() ) != 3) {
switch(opt) {
case 1: addPassword();
break;
case 2: showPasswords();
break;
© 2011 ITCourseware, LLC Rev 5.1.4 Page 527
Advanced Java Programming
Chapter 11 (cont'd)
[Link] (cont'd)
default: [Link]("Invalid Option");
}
}
savePasswords();
}
catch (Exception e) {
[Link]();
}
}
PBEKeySpec keySpec =
new PBEKeySpec([Link]());
SecretKeyFactory skf =
[Link]("PBEWithMD5AndDES");
theKey = [Link](keySpec);
cipher = [Link]("PBEWithMD5AndDES");
[Link](Cipher.DECRYPT_MODE, theKey, paramSpec);
}
[Link] (cont'd)
SealedObject so = (SealedObject) [Link]();
pw = (ArrayList<UsernamePassword>) [Link](cipher);
[Link]();
}
}
catch (Exception e) {
[Link](e);
}
return pw;
}
private int displayMenu() {
int choice = 0;
String input;
try {
[Link]("\n Menu");
[Link]("1 - Add Password");
[Link]("2 - Show Passwords");
[Link]("3 - Quit");
[Link]("\nEnter Option: ");
input = [Link]();
choice = [Link](input);
}
catch (IOException e) {
[Link](e);
}
catch (NumberFormatException e) {
[Link](e);
}
return choice;
}
private void addPassword() {
try {
UsernamePassword up;
String ws,u,p;
[Link]("Website: ");
ws = [Link]();
[Link]("Username: ");
u = [Link]();
[Link]("Password: ");
p = [Link]();
up = new UsernamePassword(ws, u, p);
[Link](up);
}
catch (Exception e) {
[Link]([Link]());
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 529
Advanced Java Programming
Chapter 11 (cont'd)
[Link] (cont'd)
}
private void showPasswords() {
Iterator<UsernamePassword> iter = [Link]();
while ([Link]())
[Link]([Link]());
}
private void savePasswords() throws Exception {
ObjectOutputStream ois = new ObjectOutputStream(
new FileOutputStream("[Link]"));
[Link](Cipher.ENCRYPT_MODE, theKey, paramSpec);
SealedObject so = new SealedObject(passwords, cipher);
[Link](so);
[Link]();
}
public static void main(String[] args) {
new PasswordList();
}
}
Chapter 12
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
try {
context = new LoginContext("KSJAAS", new LoginHandler());
[Link]();
subject = [Link]();
Set<Principal> principals = [Link]();
for (Principal p : principals) {
[Link]("Principal type: " +
[Link]().getName() + " name: " + [Link]());
}
}
catch (Exception e) {
[Link]();
}
}
}
[Link]
You need to change the login config file to specify
[Link].
You also need to create a policy file for the application. Because we're only
looking at the Subject, and not calling doAs(), you don't need a separate policy
entry specifying the principal. Because of that, the [Link] file in the
chapter directory will do the job.
Normally, that's all you would have to do. But since NTJAAS doesn't use a
callback handler, you need to edit the source code to pass a LoginHandler to the
LoginContext constructor.
Chapter 12 (cont'd)
[Link]
REM [Link]
java -[Link] -[Link]=..\[Link] -
[Link]=[Link] KSJAAS
[Link]
KSJAAS {
[Link] required;
};
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
String password = [Link]();
[Link]([Link]());
}
else {
throw new UnsupportedCallbackException(cb);
}
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
try {
lc = new LoginContext("EgCallback", new EgLoginHandler());
[Link]();
Subject subject = [Link]();
Object o = [Link](subject, new SalariesAction());
if (o instanceof Double) {
double d = ((Double) o).doubleValue();
[Link]("Total salary: " +
[Link]().format(d));
}
[Link]();
}
catch (Exception e) {
[Link]();
}
}
}
Chapter 12 (cont'd)
[Link]
EgCallback {
EgLoginModule required;
};
[Link]
REM [Link]
javac Eg*.java
jar cvfm [Link] [Link] Eg*.class
[Link]
Manifest-Version: 1.0
Main-Class: EgCallback
Class-Path: [Link]
[Link]
grant codeBase "[Link] {
permission [Link];
};
[Link]
REM [Link]
java -[Link] -[Link]=[Link] -
[Link]=[Link] -jar [Link]
Chapter 13
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
InitialContext ctx = new InitialContext(env);
RateInfo rates = (RateInfo) [Link]("RateInfo");
[Link]([Link]());
}
catch (RemoteException re) {
[Link](re);
}
catch (NamingException ne) {
[Link](ne);
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 13 (cont'd)
[Link] (cont'd)
[Link](Context.INITIAL_CONTEXT_FACTORY,
"[Link]");
[Link](Context.PROVIDER_URL, "rmi:///");
try {
InitialContext ctx = new InitialContext(env);
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Page 536 Rev 5.1.4 © 2011 ITCourseware, LLC
Solutions
[Link] (cont'd)
import [Link];
if ([Link] > 0) {
host = arg[0];
}
else {
[Link]("Usage: java NSLookup1 hostname");
[Link](1);
}
try {
DirContext ctxt = new InitialDirContext(env);
Attributes att = [Link](host, rrtypes);
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 13 (cont'd)
import [Link];
[Link] (cont'd)
import [Link];
if ([Link] > 0) {
host = arg[0];
if ([Link] > 1) {
rrtypes = new String[[Link] - 1];
for (int i = 1; i < [Link]; i++) {
rrtypes[i - 1] = arg[i];
}
}
}
else {
[Link]("Usage: java NSLookup2 hostname RRtype(s)");
[Link](1);
}
try {
DirContext ctxt = new InitialDirContext(env);
Attributes att = [Link](host, rrtypes);
Chapter 14
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
SAXParser parser = [Link]();
MileageHandler handler = new MileageHandler();
[Link](new File(args[0]), handler);
[Link]("The number of cars with less than 20,000 " +
"miles is " + [Link]());
}
catch (Exception e) {
[Link] ("ERROR " + e);
}
}
}
if ([Link]("car") || [Link]("van")) {
String miles = [Link]("miles");
if ( [Link](miles) < 20000) {
counter ++;
}
}
}
Chapter 14 (cont'd)
[Link] (cont'd)
public int getCount() {
return counter;
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
DocumentBuilder builder = [Link]();
Document document = [Link](new File("[Link]") );
addCar(document,"Saturn", "SL2", "2002");
}
catch (Exception e) {
[Link](e);
}
}
[Link] (cont'd)
Element year = [Link]("year");
text = [Link](carYear);
[Link](text);
[Link](make);
[Link](model);
[Link](year);
[Link]("miles", carYear);
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
DocumentBuilder builder = [Link]();
Document document = [Link](new File("[Link]") );
addCar(document,"Saturn","SL2","2002");
Chapter 14 (cont'd)
[Link] (cont'd)
TransformerFactory tFactory = [Link]();
Transformer transformer = [Link](xslSource);
[Link](domSource, result);
}
catch (Exception e) {
[Link](e);
}
}
[Link](make);
[Link](model);
[Link](year);
[Link]("miles", carYear);
Chapter 15
[Link]
public class Pager {
public native void page(String message, String id);
static {
[Link]("Pager");
}
}
[Link]
public class PagerTest {
public static void main(String args[]) {
String message = "Houston, we have a problem...";
String id = "SysDBA";
Pager.c
#include <stdio.h>
#include "Pager.h"
#include "LegacyPager.h"
pageThem(localMessage, localId);
Chapter 15 (cont'd)
[Link]
public class Pager2 {
public native void page(String message, String id);
public native String who(String id);
static {
[Link]("Pager2");
}
}
[Link]
public class PagerTest2 {
public static void main(String args[]) {
String message = "Houston, we have a problem...";
String id = "SysDBA";
Pager2.c
#include <stdio.h>
#include "Pager2.h"
#include "LegacyPager.h"
pageThem(localMessage, localId);
Pager2.c (cont'd)
JNIEXPORT jstring JNICALL Java_Pager2_who
(JNIEnv *env, jobject obj, jstring id)
{
const char *localMessage = {"Isaac Newton"};
return (*env)->NewStringUTF(env, localMessage);
}
Chapter 16
[Link]
public class IdGenerator2 {
private int counter;
private static IdGenerator2 theInstance;
private IdGenerator2() {
counter = 0;
}
[Link]
public class HumanResourcesApp2 {
public static void main(String[] args) {
NewHireHelper2 h1 = new NewHireHelper2();
NewHireHelper2 h2 = new NewHireHelper2();
[Link]();
[Link]();
}
}
[Link] (cont'd)
int id = [Link]();
Employee e = new Employee(id);
// add new employee to database
}
public void run() {
createNewEmployee();
}
}
[Link]
public class IdGeneratorFactory {
//Factory Method
public IdGenerator3 getIdGenerator() {
if ([Link]()) {
return [Link]();
}
else {
return [Link]();
}
}
}
[Link]
public class SimpleIdGenerator implements IdGenerator3 {
private int counter;
private static SimpleIdGenerator theInstance;
private SimpleIdGenerator() {
counter = 0;
}
public synchronized int getNextId() {
return counter++;
}
/*
* This method is not strictly necessary in this example, but if
© 2011 ITCourseware, LLC Rev 5.1.4 Page 547
Advanced Java Programming
Chapter 16 (cont'd)
[Link] (cont'd)
* your Singleton class were to extend a class that implemented
* clone(), without this method it would be possible to create more
* than one instance via cloning.
*/
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
[Link]
import [Link];
import [Link];
import [Link];
static {
Properties p = new Properties();
try {
FileInputStream inputStream =
new FileInputStream("[Link]");
if (inputStream != null) {
[Link](inputStream);
String value = [Link]("next");
Integer i = new Integer(value);
valid = true;
next = [Link]();
}
}
catch (NumberFormatException nfe) {
valid = false;
}
catch (IOException e) {
valid = false;
}
}
[Link] (cont'd)
return valid;
}
public static int getNext() {
return next;
}
}
[Link]
public class FileIdGenerator implements IdGenerator3 {
private int counter;
private static FileIdGenerator theInstance;
private FileIdGenerator() {
counter = [Link]();
}
[Link]
public class HumanResourcesApp3 {
public static void main(String[] args) {
NewHireHelper3 h1 = new NewHireHelper3();
NewHireHelper3 h2 = new NewHireHelper3();
[Link]();
© 2011 ITCourseware, LLC Rev 5.1.4 Page 549
Advanced Java Programming
Chapter 16 (cont'd)
[Link] (cont'd)
[Link]();
}
}
[Link]
public interface IdGenerator3 {
public int getNextId();
}
[Link]
next=22
[Link]
public class BuilderClient {
public static void main(String[] args) {
Director d = new Director();
Builder b = [Link]("FlatFile");
[Link](b);
OutputFile flat = [Link]();
[Link]("[Link]");
b = [Link]("PropertyFile");
[Link](b);
OutputFile prop = [Link]();
[Link] (cont'd)
[Link]("[Link]");
[Link]
public class Director {
private Builder builder;
private Manager manager;
public Director() {
manager = new Manager(42);
[Link]("303 867-5309");
[Link]("Gunnison");
}
[Link]
public abstract class Builder {
Chapter 16 (cont'd)
[Link]
public class PropertyBuilder extends Builder {
private PropertyFile file;
public PropertyBuilder() {
file = new PropertyFile();
}
[Link]
public class FlatFileBuilder extends Builder {
private static String EOL = [Link]("[Link]");
FlatFile file = new FlatFile();
[Link]
public interface OutputFile {
public void write(String filename);
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Chapter 16 (cont'd)
[Link] (cont'd)
public void append(String s) {
[Link](s);
}
public void write(String file) {
try {
OutputStreamWriter osw =
new OutputStreamWriter(new FileOutputStream(file));
BufferedWriter bw = new BufferedWriter(osw);
[Link]([Link]());
[Link]();
}
catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Chapter 17
[Link]
import [Link].*;
private IOFacade2() {
}
try {
File file = new File(filename);
out = new ObjectOutputStream(
new FileOutputStream(file, append));
[Link](obj);
}
catch (IOException e) {
log(e);
throw e;
}
finally {
[Link]();
}
}
public void writeToTextFile(String filename, String text)
throws IOException {
try {
File file = new File(filename);
© 2011 ITCourseware, LLC Rev 5.1.4 Page 555
Advanced Java Programming
Chapter 17 (cont'd)
[Link] (cont'd)
out = new PrintWriter(new BufferedWriter(
new FileWriter(file, append)));
[Link](text);
}
catch (IOException e) {
log(e);
throw e;
}
finally {
[Link]();
}
}
public void writeToBinaryFile(String filename, byte[] bytes)
throws IOException {
try {
File file = new File(filename);
out = new BufferedOutputStream(
new FileOutputStream(file, append));
[Link](bytes);
}
catch (IOException e) {
log(e);
throw e;
}
finally {
try {
[Link]();
}
catch (IOException ioe) {
log(ioe);
throw ioe;
}
}
}
private void log(Exception e) {
try {
writeToTextFile("[Link]", [Link]());
}
Page 556 Rev 5.1.4 © 2011 ITCourseware, LLC
Solutions
[Link] (cont'd)
catch (IOException ioe) {
[Link](ioe);
}
}
}
[Link]
public class FacadeClient2 {
public static void main(String[] args) {
IOFacade2 facade = [Link]();
try {
[Link]("[Link]", text);
[Link]("[Link]", text);
[Link]("[Link]", bytes);
}
catch (Exception e) {
[Link](e);
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
public GenericArrayAdapter2() {
adaptee = new GenericArray<E>();
}
public GenericArrayAdapter2(int initialSize) {
adaptee = new GenericArray<E>(initialSize);
}
Chapter 17 (cont'd)
[Link] (cont'd)
public boolean addAll(Collection<? extends E> c) {
Iterator<? extends E> it = [Link]();
while ([Link]()) {
add([Link]());
}
return true;
}
public void clear() {
[Link]();
}
public boolean contains(Object obj) {
if ([Link](obj) < 0) {
return false;
}
return true;
}
public boolean containsAll(Collection<?> c) {
Iterator<?> it = [Link]();
while ([Link]()) {
if (!contains([Link]())) {
return false;
}
}
return true;
}
public boolean isEmpty() {
if ([Link]() == 0)
return true;
else
return false;
}
public Iterator<E> iterator() {
[Link] (cont'd)
}
catch(IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public boolean remove(Object obj) {
int index = [Link](obj);
if (index != -1) {
[Link](index);
return true;
}
return false;
}
public boolean removeAll(Collection<?> c) {
boolean flag = false;
Iterator<?> it = [Link]();
while ([Link]()) {
if (remove([Link]())) {
if (flag == false) {
flag = true;
}
}
}
return flag;
}
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
public int size() {
return [Link]();
}
public Object[] toArray() {
return [Link]();
}
@SuppressWarnings("unchecked") // Not supported in 1.5.0
public <T> T[] toArray(T[] array){
// make sure array is big enough
int size = [Link]();
Chapter 17 (cont'd)
[Link] (cont'd)
if ([Link] < size) {
array = (T[])[Link](
[Link]().getComponentType(), size);
}
// add elements to array
Iterator<E> it = [Link]();
Object[] temp = array;
for (int i = 0; i < size; i++) {
temp[i] = [Link]();
}
// add null terminator
if ([Link] > size) {
array[size] = null;
}
return array;
}
public boolean equals(Object o) {
return [Link](o);
}
public int hashCode() {
return [Link]();
}
}
[Link]
import [Link].*;
[Link] (cont'd)
for (int i = 0; i < 10; i++) {
[Link]("Element: " + i);
}
}
private static void removeItemFromCollection(Collection<String> c) {
[Link]("Element: 8");
}
private static void removeItemsFromCollection(Collection<String> c){
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < 5; i++) {
[Link]("Element: " + i);
}
[Link](list);
}
private static void printCollection(Collection<String> c) {
Iterator it = [Link]();
while ([Link]()) {
[Link]([Link]());
}
}
}
[Link]
public abstract class ArithmeticComponent2 {
public void add(ArithmeticComponent2 component) {
throw new UnsupportedOperationException();
}
public void remove(ArithmeticComponent2 component) {
throw new UnsupportedOperationException();
}
public ArithmeticComponent2 getChild(int i) {
throw new UnsupportedOperationException();
}
public int evaluate() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
[Link]
import [Link];
import [Link];
Chapter 17 (cont'd)
[Link] (cont'd)
public class Operator2 extends ArithmeticComponent2 {
private ArrayList<ArithmeticComponent2> children;
private String operator;
if (firstChild == true) {
result = [Link]();
firstChild = false;
continue;
}
if ([Link]("+"))
result += [Link]();
else if ([Link]("-"))
result -= [Link]();
else if ([Link]("*"))
result *= [Link]();
else if ([Link]("/"))
result /= [Link]();
}
return result;
}
public void print() {
Iterator<ArithmeticComponent2> it = [Link]();
Page 562 Rev 5.1.4 © 2011 ITCourseware, LLC
Solutions
[Link] (cont'd)
while ([Link]()) {
ArithmeticComponent2 comp = [Link]();
[Link]();
if ([Link]())
[Link](" " + operator + " ");
}
}
}
[Link]
public class Operand2 extends ArithmeticComponent2 {
private int op;
[Link]
public class CompositeClient2 {
public static void main(String[] args) {
// 5 * 12 + 3 * 9
ArithmeticComponent2 expression = new Operator2("+");
ArithmeticComponent2 subExpression1 = new Operator2("*");
ArithmeticComponent2 subExpression2 = new Operator2("*");
[Link](subExpression1);
[Link](new Operand2(5));
[Link](new Operand2(12));
[Link](subExpression2);
[Link](new Operand2(3));
[Link](new Operand2(9));
[Link]();
[Link]("= " + [Link]());
}
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 563
Advanced Java Programming
Chapter 18
[Link]
import [Link];
import [Link];
import [Link];
while ([Link]()) {
for (int i = 1; i < numColumns; i++) {
[Link]([Link](i) + "\t");
}
[Link]();
}
[Link]();
}
}
[Link]
public class TemplateClient2 {
public static void main(String[] args) {
String driverClass = "[Link]";
String url = "jdbc:derby://localhost:1527/j2se";
String username = null;
String password = null;
DatabaseAccessTemplate db = new Query();
[Link] (cont'd)
String sql = "SELECT * FROM Employee";
[Link](driverClass, url, sql, username, password);
}
}
[Link]
public class StandByState2 implements State {
private Computer2 computer;
[Link]
public class Computer2 {
State activeState = new ActiveState2(this);
State hibernateState = new HibernateState2(this);
State shutdownState = new ShutdownState2(this);
State standByState = new StandByState2(this);
State currentState;
Chapter 18 (cont'd)
[Link] (cont'd)
public Computer2() {
currentState = shutdownState;
}
public void hibernate() {
[Link]();
}
public void keyPress() {
[Link]();
}
public void powerOn() {
[Link]();
}
public void shutdown() {
[Link]();
}
public void standBy() {
[Link]();
}
public void setState(State state) {
currentState = state;
}
public State getActiveState() {
return activeState;
}
public State getHibernateState() {
return hibernateState;
}
public State getShutdownState() {
return shutdownState;
}
public State getStandByState() {
return standByState;
}
}
[Link]
public class StateClient2 {
public static void main(String[] args) {
Computer2 comp = new Computer2();
[Link] (cont'd)
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
[Link]();
}
}
[Link]
public class ActiveState2 implements State {
private Computer2 computer;
Chapter 18 (cont'd)
[Link]
public class HibernateState2 implements State {
private Computer2 computer;
[Link]
public class ShutdownState2 implements State {
private Computer2 computer;
[Link] (cont'd)
[Link]("Starting up. . .");
[Link]([Link]());
}
public void shutdown() {
[Link]("You can't shutdown a shutdown computer");
}
public void standBy() {
[Link]("You can't standby a shutdown computer");
}
}
[Link]
public class Logger implements Observer {
private Subject subject;
[Link]
import [Link];
import [Link];
import [Link];
public TellerGUI2() {
[Link]("Teller");
BankAccount account = new BankAccount(0);
// New code:
Logger logger = new Logger(account);
Chapter 18 (cont'd)
[Link] (cont'd)
initializePanels(account);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
// trigger first notify
[Link](0);
}
private void initializePanels(Subject subject) {
currentBalancePanel = new CurrentBalancePanel(subject);
modifyBalancePanel = new ModifyBalancePanel(subject);
statusPanel = new StatusPanel(subject);
}
public static void main(String[] args) {
new TellerGUI2();
}
}
Appendix A
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
String sqltxt;
sqltxt = "SELECT FIRSTNAME, LASTNAME, SALARY FROM EMPLOYEE";
ResultSet rs = [Link](sqltxt);
String name;
String dept;
float salary;
float totalsalary = 0.0F;
while ([Link]()) {
name = [Link](1) + " " + [Link](2);
salary = [Link](3);
totalsalary += salary;
[Link]("%1s \t %2.2f %n", name, salary);
}
[Link]("----------\nTotal salary: " + totalsalary);
[Link]();
[Link]();
}
catch (ClassNotFoundException cnfe) {
[Link]("Unable to load driver class: " + cnfe);
}
catch (SQLException sqle) {
[Link](sqle);
}
}
}
© 2011 ITCourseware, LLC Rev 5.1.4 Page 571
Advanced Java Programming
Appendix A (cont’d)
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
String sqltxt;
sqltxt = "SELECT FIRSTNAME, LASTNAME, SALARY, DEPARTMENT_NAME " +
"FROM EMPLOYEE, DEPARTMENT " +
"WHERE EMPLOYEE.DEPARTMENT_CODE = DEPARTMENT.DEPARTMENT_CODE";
ResultSet rs = [Link](sqltxt);
String name;
String dept;
float salary;
float totalsalary = 0.0F;
float tempsal;
Map<String, Float> map = new HashMap<String, Float>();
while ([Link]()) {
name = [Link](1) + " " + [Link](2);
salary = [Link](3);
totalsalary += salary;
if ([Link](dept)) {
// unboxing and generics simplify this process
Page 572 Rev 5.1.4 © 2011 ITCourseware, LLC
Solutions
[Link] (cont'd)
tempsal = [Link](dept);
}
else {
tempsal = 0.0F;
}
[Link](dept, new Float(tempsal + salary));
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Appendix A (cont’d)
[Link] (cont'd)
[Link]("[Link]");
String url = "jdbc:derby://localhost:1527/j2se";
conn = [Link](url);
Statement stmt = [Link]();
String sqltxt;
sqltxt = "SELECT FIRSTNAME, LASTNAME, SALARY, DEPARTMENT_NAME " +
"FROM EMPLOYEE, DEPARTMENT " +
"WHERE EMPLOYEE.DEPARTMENT_CODE = DEPARTMENT.DEPARTMENT_CODE";
ResultSet rs = [Link](sqltxt);
String name;
String dept;
float salary;
float totalsalary = 0.0F;
float tempsal;
Map<String, Float> map = new HashMap<String, Float>();
while ([Link]()) {
name = [Link](1) + " " + [Link](2);
salary = [Link](3);
totalsalary += salary;
[Link]([Link](name, 30));
[Link]("%1.2f %n", salary);
dept = [Link](4);
if ([Link](dept)) {
// unboxing and generics simplify this process
tempsal = [Link](dept);
}
else {
tempsal = 0.0F;
}
[Link](dept, new Float(tempsal + salary));
}
[Link] (cont'd)
[Link]("\nTotal Salary for Department");
Set<String> keys = [Link]();
Iterator<String> it = [Link]();
while ([Link]() ) {
dept = [Link]();
[Link]("%1.2f \t %2s %n", [Link](dept), dept);
}
}
catch (ClassNotFoundException cnfe) {
[Link]("Unable to load driver class: " + cnfe);
}
catch (SQLException sqle) {
[Link](sqle);
}
}
}
[Link]
import [Link];
import [Link];
Appendix A (cont’d)
[Link] (cont'd)
if (w != null) {
[Link]("SQL Warning:");
while (w != null) {
[Link](formatSQLException(w));
w = [Link]();
}
return true;
}
else {
return false;
}
}
public static String rPadTrunc(String s, int len) {
return rPadTrunc(s, len, ' ');
}
public static String rPadTrunc(String s, int len, char c) {
if ( s == null ) {
s = "";
}
if (len <= [Link]()) {
return [Link](0,len);
}
StringBuilder sb = new StringBuilder(s);
for (int k=0; k < (len - [Link]()); k++) {
[Link](c);
}
return [Link]();
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
[Link] (cont'd)
public class DeptEmps {
public static void main (String args []) {
Connection conn;
try {
String dept = "";
try {
BufferedReader br = new BufferedReader(
new InputStreamReader([Link]));
[Link]("Enter a dept code and press <Return>: ");
dept = [Link]();
if (dept == null) {
[Link]("No department entered. Exiting.");
[Link](0);
}
} catch(IOException ioe) {
[Link]();
}
[Link]("[Link]");
conn = [Link]
("jdbc:derby://localhost:1527/j2se");
if ([Link]()) {
[Link]("Employees of department " + dept + ":");
do {
String id = [Link](1);
String name = [Link](2) + " " + [Link](3);
String title = [Link](4);
[Link](id + "\t" + name + "\t" + title);
} while ([Link]());
}
else {
[Link]("No employees found for " + dept + ".");
}
[Link]();
[Link]();
[Link]();
}
catch (ClassNotFoundException cnfe) {
[Link]("Unable to load driver class: " + cnfe);
Appendix A (cont’d)
}
catch (SQLException sqle) {
[Link](sqle);
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
try {
BufferedReader br = new BufferedReader(
new InputStreamReader([Link]));
[Link]("Enter a SQL statement. Enter 'q' to quit");
[Link] (cont'd)
Connection conn = [Link](url);
Statement stmt = [Link]();
int count = 0;
if ([Link]().toUpperCase().startsWith("SELECT")) {
ResultSet rs = [Link](sqlString);
while ([Link] ()) {
count++;
}
[Link]();
[Link]("%n%1d row(s) retrieved.%n", count);
}
else {
count = [Link](sqlString);
[Link]("%n%1d row(s) updated.%n", count);
}
[Link]();
[Link]();
}
catch (ClassNotFoundException cnfe) {
[Link]("Unable to load driver class: " + cnfe);
}
catch (SQLException sqle) {
[Link](sqle);
}
}
}
[Link]
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
Appendix A (cont’d)
[Link] (cont'd)
String sqlString = "";
try {
BufferedReader br = new BufferedReader(
new InputStreamReader([Link]));
[Link]("Enter a SQL statement. Enter 'q' to quit");
int count = 0;
if ([Link]().toUpperCase().startsWith("SELECT")) {
ResultSet rs = [Link](sqlString);
ResultSetMetaData rsmd = [Link]();
int cols = [Link]();
[Link] (cont'd)
}
else {
count = [Link](sqlString);
[Link]("%n%1d row(s) updated.%n", count);
}
[Link]();
[Link]();
}
catch (ClassNotFoundException cnfe) {
[Link]("Unable to load driver class: " + cnfe);
}
catch (SQLException sqle) {
[Link](sqle);
}
}
}
Index
U
UDP 110
unbind 274
UnicastRemoteObject 138, 141
[Link] 134
UNIX 320, 323
9-00-00017-000-08-05-11
*9-00-00017-000-08-05-11*
Java RMI facilitates client-server application development by enabling method calls on an object that exists in a different JVM, making the remote interaction appear as if it were local. In this model, a client looks up the remote object in the server's registry and uses the object's methods. Stubs and skeletons play essential roles: the stub on the client side acts as a local representative or proxy and forwards calls to the remote object; the skeleton on the server side receives incoming requests, processes them, and sends responses back to the stub. This abstraction simplifies remote method invocation by managing low-level network communication seamlessly .
In a pre-Java 5 environment, developers had to manually generate stub classes by using the `rmic` compiler. In Java 5 and above, the Java platform supports automatic stub generation. The stub class is automatically generated by the JVM when the remote object is exported, provided the stub class is not already available in the classpath. However, `rmic` is still required if there is a need to support clients running on a pre-Java 5 VM .
Java RMI uses exception handling extensively to manage remote object references, given the potential issues in remote communication. Key exceptions include `java.rmi.AccessException` if the calling code lacks permission, `java.net.MalformedURLException` for invalid URL formats, `java.rmi.UnknownHostException` if the host is not available, `java.rmi.RemoteException` for unavailable server registries, and `java.rmi.NotBoundException` if an object with the requested name is not found in the registry. This structured use of exceptions allows developers to gracefully handle different areas of failure during remote method invocation .
Java design patterns, specifically creational patterns, are vital in encapsulating the process of object instantiation, allowing this process to change without impacting the rest of the system. They lead to more flexible and reusable code by segregating the construction logic from the client. The advantages include capturing expert knowledge, facilitating system restructuring, avoiding duplicate code, and providing a common vocabulary among developers. By using abstract classes or interfaces, client code remains unaffected by changes in the concrete implementation, which also allows for improved code maintainability and scalability .
The RMI Registry is a server-side service in Java RMI that binds a name to a remote object. It tracks the remote objects available on a particular host. Multiple servers can register objects with the registry, and it can either bind or rebind objects using methods like `Naming.bind()` or `Naming.rebind()`. Programmatically, developers interact with the registry by registering remote objects with these methods and fetching them using the `Naming.lookup()` method. This allows clients to obtain references to remote objects using a URL format .
Local methods in Java are always executed directly within the same machine, where objects are passed by reference and primitive datatypes by value. For remote methods, objects implementing the Remote interface are passed by reference and need to be exported before being used as parameters or returned; these objects do not need binding to a registry. Remote methods must handle RemoteExceptions due to network transmission, and objects implementing Serializable are serialized and passed by value, with primitive datatypes also passed by value .
The Factory Method pattern supports the dynamic selection of a builder class in systems utilizing the builder pattern by determining which specific builder to instantiate based on input parameters. This pattern creates an instance of a class implementing a common interface without depending on its specific class, allowing the system to decide at runtime which builder class to use. This is helpful when a system must support multiple complex product types or when these types might change, as it separates the construction logic from the product instantiation logic .
Behavioral patterns in Java differ from structural and creational patterns as they focus on the interaction and collaboration between objects rather than the system's structure or the creation of objects. Their primary purpose is to enable distribution of responsibility among objects, defining how they communicate, manage task assignments, and encapsulate control flows. This separation addresses the object communication dynamics, resulting in systems that are more flexible regarding interactions and behaviors. Behavioral patterns also encourage encapsulation of algorithms and promote an easier refactoring process when behavior needs to change .
The Composite design pattern is most beneficial in Java-based applications when dealing with hierarchical data structures like file systems, UI components, or document structures. Its ability to treat individual objects and compositions of objects uniformly makes it especially useful when the application needs to process tree structures. By using recursion, the pattern simplifies client code, as it allows clients to work with complex tree structures without needing separate logic for leaf and composite nodes. The pattern is also suitable for scenarios that require adding new types of components without altering the existing code structure, enhancing the system's scalability and flexibility .
To set up and run a Java RMI application, follow these steps: 1) Define the remote interface extending `java.rmi.Remote`. 2) Implement the interface in a server class, having constructors that throw `RemoteException`. 3) Compile the Java files with `javac` to produce class files. 4) Use `rmic` to generate the stub and skeleton classes if not using Java 5 or later, where dynamic generation is supported. 5) Start the RMI Registry using `rmiregistry`. 6) Register the server object within the RMI Registry using `Naming.bind()` or `Naming.rebind()`. 7) Start the server application. 8) Run the client application, which will look up the remote object and invoke methods as needed .