+
+
+Sun Certified Developer for the Java 2 Platform: Application Submission (Version 1.1.3)
+
+
+
+Sun Certified Developer for the Java 2 Platform: Application
+Submission (Version 1.1.3)
+
+Introduction and Index
+This document tells you what you need, and what you must do, to submit
+your solution to the Sun Certified Developer for the Java 2 Platform programming
+assignment. Read it carefully before you begin work on the solution. This
+document contains strict guidelines about the way the work is to be performed.
+These guidelines ensure consistency and fairness.
+
The application distribution is composed of:
+
+
+This document
+
+A non-relational database file
+
+Be sure to maintain a backup copy of all files related to your project,
+including the distribution files, until you receive your certificate in
+case one or more is corrupted, lost, or becomes unusable. You must not
+use any materials issued to other certification candidates even if you
+believe they are identical.
+
This document is divided into the following sections:
+
+
+Application Overview - A general description
+of the application you will write
+
+Architecture - The parts of the application
+and how they fit together
+Where this document uses the word "must" an absolute requirement is
+being described. If you fail to adhere to such a requirement, your assignment
+will be failed automatically, and without further evaluation. It is therefore
+imperative that you pay close attention to any statement using the word
+"must" in this document. Portions of your submission will be analyzed
+by software; where a specific spelling or structure is required, even a
+slight deviation could result in automatic failure.
+
+
+Application Overview
+
Background
+URLyBird is a broker of discount hotel rooms. They sell
+accomodations for business and pleasure travellers at short notice,
+helping hotels to fill rooms that would otherwise be left empty. They
+take bookings only within 48 hours of the start of room occupancy.
+Curently, URLyBird sells the rooms over the phone using a team of
+customer service representatives (CSRs). The CSRs interact with an
+aging custom-written application that has been drawing increasing criticism
+from the CSRs. In the future, URLyBird wants to move into
+Internet-based marketing, and hopes to be able to accept bookings
+direct from customers over the web.
+
The company's IT director has decided to migrate the existing
+application to a Java technology based system. Initially, the system
+will support only the CSRs, although the hope is that this interim
+step will give them a starting point for migrating the system to the
+web. The IT director does not anticipate much reuse of the first Java
+technology system, but intends to use that system as a learning
+exercise before going on to a web based system.
+
The company's IT department has a data file that contains the
+essential information for the company, but because the data must
+continue to be manipulated for reports using another custom-written
+application, the new system must reimplement the database code from
+scratch without altering the data file format.
+
The new application, using the existing data file format, must
+allow the CSRs to generate a list of accomodations that match a
+customer's criteria. This is the project that you have been
+commissioned to implement.
+
+What you must do
+The following are the "top level" features that must be implemented:
+
+
+A client program with a graphical user interface that connects to the database
+
+A data access system that provides record locking and a flexible search
+mechanism
+
+Network server functionality for the database system
+
+The work involves a number of design choices that have to be made. In all
+such cases, the following principles should be applied.
+
+Clarity and Maintainability
+
+
+A clear design, such as will be readily understood by junior programmers,
+will be preferred to a complex one, even if the complex one is a little
+more efficient. Code complexity, including nesting depth, argument passing,
+and the number of classes and interfaces, should be reasonable.
+
+
+
+
+Documentation
+
+
+The code itself should be as clear as possible; do not provide comments
+that do not add to the comprehensibility of the code. Awkward or complex
+code should have descriptive comments, and javadoc style comments must
+be used for each element of the public interface of each class. You must
+create a full suite of documentation for the classes of the completed project.
+This must be generated using the tool "javadoc" and must be in HTML format.
+Provide javadoc documentation for all classes you write.
+
+
+
+You must provide basic user documentation. This should be sufficient to
+allow a user who is familiar with the broad purpose of the project to use
+the application. This documentation must be in one of these three formats:
+
+
+
+HTML
+
+Plain text (not a wordprocessor format)
+
+Within the application as a help system.
+
+
+Correctness
+
+Your project must conform to this specification. Features that deviate
+from specification will not receive full credit. You will not receive
+extra credit points for work beyond the requirements of the specification.
+
+Use of Standard Elements
+
Use of functionality provided by the core Java classes will be preferred
+to your own implementation of that functionality, unless there is a specific
+advantage to providing your own implementation.
+The main architecture of the application must be a traditional client-server
+system. There are three key parts: the server-side data management
+system, the client-side GUI, and the network connection between the two.
+
+Non-Networked Mode
+The program must be able to work in a non-networked mode. In this mode,
+the database and GUI must run in the same VM and must perform no networking,
+must not use loopback networking, and must not involve the serialization
+of any objects when communicating between the GUI and database elements.
+
The operating mode is selected using the single command line argument
+that is permitted. Architecturally, this mode must use the database and
+GUI from the networked form, but must not use the network server code at
+all.
+
+Network Communication Approach
+You have a choice regarding the network connection protocol. You must use
+either serialized objects over a simple socket connection, or RMI.
+Both options are equally acceptable. Keep in mind that networking
+must be entirely bypassed in the non-networked mode.
+
+Restrictions on RMI
+To avoid unnecessary complexity in the marking environment certain restrictions
+are placed on solutions that use RMI. Specifically:
+
+
+ You must not require the use of an HTTP server.
+
+ You must not require the installation of a security manager.
+
+ You must provide all classes pre-installed so that no dynamic class
+downloading occurs.
+The user interface for this assignment must satisfy the following criteria:
+
+
+It must be composed exclusively with components from the Java Foundation
+Classes (Swing components).
+
+It must allow the user to search the data for all records, or for records
+where the name and/or location fields exactly match values specified by
+the user.
+
+It must present search results in a JTable.
+
+It must allow the user to book a selected record, updating the database
+file accordingly.
+
+Your user interface should be designed with the expectation of future functionality
+enhancements, and it should establish a framework that will support this
+with minimal disruption to the users when this occurs.
+
+Your data access class must be called "Data.java", must be in a package called "suncertify.db", and must implement the following interface:
+
+package suncertify.db;
+public interface DB
+{
+// Reads a record from the file. Returns an array where each
+// element is a record value.
+public String[] read(int recNo) throws RecordNotFoundException;
+// Modifies the fields of a record. The new value for field n
+// appears in data[n]. Throws SecurityException
+// if the record is locked with a cookie other than lockCookie.
+public void update(int recNo, String[] data, long lockCookie)
+throws RecordNotFoundException, SecurityException;
+// Deletes a record, making the record number and associated disk
+// storage available for reuse.
+// Throws SecurityException if the record is locked with a cookie
+// other than lockCookie.
+public void delete(int recNo, long lockCookie)
+throws RecordNotFoundException, SecurityException;
+// Returns an array of record numbers that match the specified
+// criteria. Field n in the database file is described by
+// criteria[n]. A null value in criteria[n] matches any field
+// value. A non-null value in criteria[n] matches any field
+// value that begins with criteria[n]. (For example, "Fred"
+// matches "Fred" or "Freddy".)
+public int[] find(String[] criteria);
+// Creates a new record in the database (possibly reusing a
+// deleted entry). Inserts the given data, and returns the record
+// number of the new record.
+public int create(String[] data) throws DuplicateKeyException;
+// Locks a record so that it can only be updated or deleted by this client.
+// Returned value is a cookie that must be used when the record is unlocked,
+// updated, or deleted. If the specified record is already locked by a different
+// client, the current thread gives up the CPU and consumes no CPU cycles until
+// the record is unlocked.
+public long lock(int recNo) throws RecordNotFoundException;
+// Releases the lock on a record. Cookie must be the cookie
+// returned when the record was locked; otherwise throws SecurityException.
+public void unlock(int recNo, long cookie)
+throws RecordNotFoundException, SecurityException;
+}
+
+
Any unimplemented exceptions in this interface must all be created as member classes of the
+suncertify.db package. Each must have a zero argument constructor and a second
+constructor that takes a String that serves as the exception's description.
+
Any methods that throw RecordNotFoundException should do so if a specified
+record does not exist or is marked as deleted in the database file.
+
+Network Approaches
+Your choice of RMI or serialized objects will not affect your grade, but
+no other approach is acceptable. In either case, the program must allow
+the user to specify the location of the database, and it must also accept
+an indication that a local database is to be used, in which case, the networking
+must be bypassed entirely. No authentication is required for database access.
+
+Locking
+Your server must be capable of handling multiple concurrent requests, and
+as part of this capability, must provide locking functionality as specified
+in the interface provided above. You may assume that at any moment,
+at most one program is accessing the database file; therefore your locking
+system only needs to be concerned with multiple concurrent clients of your
+server. Any attempt to lock a resource that is already locked should
+cause the current thread to give up the CPU, consuming no CPU cycles until
+the desired resource becomes available.
+
+The format of data in the database file is as follows:
+
Start of file
+ 4 byte numeric, magic cookie value. Identifies this as a data file
+ 2 byte numeric, number of fields in each record
+
Schema description section.
+ Repeated for each field in a record:
+ 1 byte numeric, length in bytes of field name
+ n bytes (defined by previous entry), field name
+ 1 byte numeric, field length in bytes
+ end of repeating block
+
Data section.
+ Repeat to end of file:
+ 1 byte flag. 00 implies valid record, 0xFF implies deleted record
+ Record containing fields in order specified in schema section, no separators
+between fields, each field fixed length at maximum specified in schema
+information
+
End of file
+
All numeric values are stored in the header information use the formats
+of the DataInputStream and DataOutputStream classes. All text values, and
+all fields (which are text only), contain only 8 bit characters, null terminated
+if less than the maximum length for the field. The character encoding is
+8 bit US ASCII.
+
Database schema
+The database that URLyBird uses contains the following fields:
+
+
+
Field descriptive name
+
Database field name
+
Field length
+
Detailed description
+
+
+
Hotel Name
+
name
+
64
+
The name of the hotel this vacancy record relates to
+
+
+
City
+
location
+
64
+
The location of this hotel
+
+
+
Maximum occupancy of this room
+
size
+
4
+
The maximum number of people permitted in this room, not including infants
+
+
+
Is the room smoking or non-smoking
+
smoking
+
1
+
Flag indicating if smoking is permitted. Valid values are "Y" indicating a smoking room,
+and "N" indicating a non-smoking room
+
+
+
Price per night
+
rate
+
8
+
Charge per night for the room. This field includes the currency symbol
+
+
+
Date available
+
date
+
10
+
The single night to which this record relates, format is yyyy/mm/dd.
+
+
+
Customer holding this record
+
owner
+
8
+
The id value (an 8 digit number) of the customer who has booked this.
+Note that for this application, you should assume that customers and CSRs
+know their customer ids. The system you are writing does not interact with
+these numbers, rather it simply records them. If this field is all blanks, the
+record is available for sale.
+Throughout this exercise, you must use exclusively the Java 2 platform.
+You may develop your code using any implementation of the Java 2 platform,
+but the submission that you return must have been tested and shown to work
+under a production (not development) version of the Sun Microsystems' Java
+2 platform and that platform must not have been superseded by a new production
+version for more than 18 months by the time you make your submission.
+
You are permitted to use any IDE tool you choose, but you must not submit
+any code that is not your own work. The final program must have no dependencies
+on any libraries other than those of the Java 2 Platform.
+
When you submit your assignment, each part (client and server) must
+be executable using a command of this exact form:
+
java -jar <path_and_filename> [<mode>]
+Your programs must not require use of command line arguments other than
+the single mode flag, which must be supported. Your programs must
+not require use of command line property specifications. All configuration
+must be done via a GUI, and must be persistent between runs of the program.
+Such configuration information must be stored in a file called suncertify.properties
+which must be located in the current working directory.
+
The mode flag must be either "server", indicating the server program
+must run, "alone", indicating standalone mode, or left out entirely, in
+which case the network client and gui must run.
+
You must not require manual editing of any files by the examiners.
+
+Packaging of Submissions
+All elements of your submission must be packaged in a single JAR file.
+The JAR file must have the following layout and contents in its root:
+
+
+The executable JAR containing the programs. This must be called runme.jar.
+
+The original, unchanged database file that was supplied to you.
+Note that you must keep a copy of the original database file supplied to
+you, and this must be the file you submit. The marking process will expect
+the exact same data without any changes.
+
+A directory called code, containing all the source code and related
+parts of your project. You must create subdirectories within this to reflect
+your package structure and distribute your source files within those directories.
+
+A file called version.txt. This must contain pure ASCII (not a
+word processor format) indicating the exact version of JDK you used, and
+the host platform you worked on.
+
+A directory called docs, containing the following items at the
+top level:
+
+
+This html file.
+
+A subdirectory called javadoc, containing HTML/Javadoc documentation for
+all classes and interfaces you are submitting.
+
+A file called choices.txt that containing pure ASCII (not a word
+processor format) text describing the significant design choices you made.
+Detail the problems you perceived, the issues surrounding them, your value
+judgments, and the decisions that you made. This document should also describe
+any uncertainties you had regarding the project, and the decisions you
+made when resolving them.
+
+User documentation for the database server and the gui client. If your
+user documentation is online then you may omit this file. However, if the
+documentation is not online, you must provide either a single plain ASCII
+(not word processor format) text document, which must be called userguide.txt,
+or multiple HTML files which must all be accessible from a starting point
+document that must be called userguide.html.
+This section describes how your submission will be marked, and the marking
+criteria which govern allocation of marks for the Sun Certified Developer
+for the Java 2 platform application submission. The first part describes
+the marking process, and the second describes how the marks are allocated.
+
How The Assignment is Marked
+
The marking is done in three phases. First, software checks that
+overall structure and nomenclature conform to specification. Second
+the examiner runs the code ensuring that it functions correctly through
+the specified operations. If any automatic failures are noted at this stage,
+the marking process terminates and the assignment is failed.
+
Provided the essential behavioral requirements of the assignment have
+been correctly implemented, the examiner proceeds to investigate the design
+and implementation of your assignment. This process is time consuming,
+and it is because this is done carefully and thoroughly that submissions
+take time to grade. The grading process is closely controlled to ensure
+consistency and fairness, and it is performed according to criteria detailed
+in the next section. At any time during this process, if an automatic failure
+is noted, the marking process terminates, and the assignment is failed.
+For any design choice concerning topics not specifically described in the
+requirements, marks are awarded for a clear and consistent approach, rather
+than for any particular solution. Design decisions must be described briefly
+but clearly in your comments.
+
In addition to the submission, you will be required to take a written
+examination. This exam tests your understanding of your submission and
+asks you to justify a number of design choices embodied in that submission.
+
+Automatic Failures
+As noted at the beginning of this document, where this document uses
+the word "must" an absolute requirement is being described. If you fail
+to adhere to such a requirement, your assignment will be failed automatically,
+and without further evaluation. It is therefore imperative that you pay
+close attention to any statement using the word "must" in this document.
+
+Marking Criteria
+Your work will be evaluated based on the following criteria. The minimum
+passing score is 320 out of a possible 400 points.
+
General Considerations (100 points)
+ Documentation (70 points)
+ Object-orietned design (30 points)
+ User Interface (40 points)
+ Locking (80 points)
+ Data class (40 points)
+ Server (40 points)
+
+
+
+You might find that you want to ask for further explanation of some part
+of this document, perhaps to seek permission to solve a problem in a particular
+way. This document deliberately leaves some issues unspecified, and some
+problems unraised. Your ability to think through these issues, in the face
+of realistically imperfect specifications, and come to a tenable solution
+is something upon which you are being graded.
+
In general, you should not ask your question; rather you should
+consider the options available and make a decision about how to address
+the problem yourself. This decision-making process is part of the marking
+scheme, and as such it is crucially important that you provide documentation
+of your choice. Be sure to describe the options you considered, the perceived
+benefits and weaknesses of each, and why you chose the solution you did.
+Provided you do not contravene any specification in this document you will
+not be marked on the particular choice that you made, but rather on the
+consistency of your decision making process and your adherence to other
+aspects of these notes during that decision making process.
+
If you feel you must ask your question, you should address it to who2contact@sun.com.
+Clearly indicate that the question relates to the Sun Certified Developer
+Exam, provide your candidate ID number, name, and include your return email
+address in the body of your message. Describe your issue as briefly
+as reasonably possible; you will be asked for more information if necessary.
+
Return to top
+
+
diff --git a/doc/todo.txt b/doc/todo.txt
new file mode 100644
index 0000000..4d4039e
--- /dev/null
+++ b/doc/todo.txt
@@ -0,0 +1,3 @@
+
+
+- on high load client network lib locks.
\ No newline at end of file
diff --git a/doc/version.txt b/doc/version.txt
new file mode 100644
index 0000000..141bb0b
--- /dev/null
+++ b/doc/version.txt
@@ -0,0 +1,26 @@
+
+JAVA VERSION:
+
+willemc@battleship:~/devv/jdk1.6.0_10/bin$ ./java -version
+java version "1.6.0_10"
+Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
+Java HotSpot(TM) Server VM (build 11.0-b15, mixed mode)
+
+
+
+PLATFORM VERSION:
+
+willemc@battleship:~$ uname -a
+Linux battleship 2.6.26-1-686 #1 SMP Wed Nov 26 19:14:11 UTC 2008 i686 GNU/Linux
+
+willemc@battleship:~$ cat /etc/apt/sources.list | grep main
+deb http://ftp.nl.debian.org/debian/ unstable main non-free contrib
+
+
+
+IDE VERSION:
+
+Eclipse SDK
+Version: 3.3.2
+Build id: M20080221-1800
+
diff --git a/resources/i18n-suncertify.properties b/resources/i18n-suncertify.properties
new file mode 100644
index 0000000..7edf217
--- /dev/null
+++ b/resources/i18n-suncertify.properties
@@ -0,0 +1,5 @@
+#
+# A properties file for i18n keys
+#
+
+test = i18n_test
\ No newline at end of file
diff --git a/resources/suncertify-default.properties b/resources/suncertify-default.properties
new file mode 100644
index 0000000..1c89d96
--- /dev/null
+++ b/resources/suncertify-default.properties
@@ -0,0 +1,17 @@
+#
+# Default suncertify application key properties file.
+#
+
+# The default modes for startup
+startup_mode = client
+
+# The default server and port to connect to as client
+# in server mode only server_port is used to bind
+server_host = localhost
+server_port = 9000
+
+# Cache this amount of rows.
+db_cache_size = 1000;
+
+# default timeout of lock is 2min.
+db_lock_timeout = 15000
diff --git a/src/suncertify/client/ClientController.java b/src/suncertify/client/ClientController.java
new file mode 100644
index 0000000..b83c818
--- /dev/null
+++ b/src/suncertify/client/ClientController.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.client;
+
+import suncertify.server.beans.HotelRoomManagerRemote;
+
+public class ClientController {
+
+
+
+ private HotelRoomManagerRemote hotelRoomManager = null;
+
+ /**
+ * @return the hotelRoomManager
+ */
+ public HotelRoomManagerRemote getHotelRoomManager() {
+ return hotelRoomManager;
+ }
+
+ /**
+ * @param hotelRoomManager the hotelRoomManager to set
+ */
+ public void setHotelRoomManager(HotelRoomManagerRemote hotelRoomManager) {
+ this.hotelRoomManager = hotelRoomManager;
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/client/HotelRoomTableModel.java b/src/suncertify/client/HotelRoomTableModel.java
new file mode 100644
index 0000000..5eeb9ea
--- /dev/null
+++ b/src/suncertify/client/HotelRoomTableModel.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.client;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import suncertify.models.HotelRoom;
+import suncertify.server.beans.HotelRoomManagerRemote;
+
+@SuppressWarnings("serial")
+public class HotelRoomTableModel extends AbstractTableModel {
+
+ private List data = new ArrayList(0);
+
+ private HotelRoomManagerRemote hotelRoomManager = null;
+
+ public HotelRoomTableModel(HotelRoomManagerRemote hotelRoomManager) {
+ if (hotelRoomManager==null) {
+ throw new NullPointerException("Can't get data from null hotelRoomManager");
+ }
+ this.hotelRoomManager=hotelRoomManager;
+ }
+
+ public void updateByCriteria(HotelRoom room) {
+ try {
+ data = hotelRoomManager.findByCriteria(room);
+ System.out.println("Got size from search: "+data.size());
+ this.fireTableDataChanged();
+ } catch (Exception e) {
+ throw new IllegalStateException("Could not get init all rooms: "+e.getMessage(),e);
+ }
+ }
+
+ public HotelRoom getHotelRoom(int rowIndex) {
+ return data.get(rowIndex);
+ }
+
+
+ // ============ TableModel
+
+ /**
+ * @see javax.swing.table.TableModel#getColumnCount()
+ */
+ @Override
+ public int getColumnCount() {
+ return 5;
+ }
+
+ /**
+ * @see javax.swing.table.TableModel#getRowCount()
+ */
+ @Override
+ public int getRowCount() {
+ return data.size();
+ }
+
+
+
+ /**
+ * @see javax.swing.table.AbstractTableModel#getColumnName(int)
+ */
+ @Override
+ public String getColumnName(int columnIndex) {
+ switch (columnIndex) {
+ case 0:
+ return "Name";
+ case 1:
+ return "Location";
+ case 2:
+ return "Size";
+ case 3:
+ return "Date";
+ default:
+ return "";
+ }
+ }
+
+
+
+ /**
+ * @see javax.swing.table.TableModel#getValueAt(int, int)
+ */
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ HotelRoom room = data.get(rowIndex);
+ switch (columnIndex) {
+ case 0:
+ return room.getName();
+ case 1:
+ return room.getLocation();
+ case 2:
+ return room.getSize();
+ case 3:
+ return room.getDateAvailable();
+ default:
+ return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/client/MainView.java b/src/suncertify/client/MainView.java
new file mode 100644
index 0000000..4d73a88
--- /dev/null
+++ b/src/suncertify/client/MainView.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+
+package suncertify.client;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableRowSorter;
+
+import suncertify.server.beans.HotelRoomManagerRemote;
+
+
+/**
+ *
+ * @author willemc
+ *
+ */
+public class MainView {
+
+ private HotelRoomManagerRemote hotelRoomManagerRemote = null;
+ private JFrame frame = null;
+
+ public MainView(HotelRoomManagerRemote hotelRoomManagerRemote) {
+ if (hotelRoomManagerRemote==null) {
+ throw new NullPointerException("Can't create MainView with null HotelRoomManager.");
+ }
+ this.hotelRoomManagerRemote=hotelRoomManagerRemote;
+
+ frame = new JFrame();
+
+ frame.setTitle("URLyBird HotelRoom Manager");
+ //frame.setIconImage(Toolkit.getDefaultToolkit().createImage(getClass().getResource("/resources/images/logos/gabelfresser.gif")));
+ frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ frame.addWindowListener(new WindowListener() {
+ public void windowActivated(WindowEvent e) { }
+ public void windowClosed(WindowEvent e) { }
+ public void windowClosing(WindowEvent e) { System.exit(0); }
+ public void windowDeactivated(WindowEvent e) { }
+ public void windowDeiconified(WindowEvent e) { }
+ public void windowIconified(WindowEvent e) { }
+ public void windowOpened(WindowEvent e) { }
+ });
+ frame.pack();
+ frame.setBounds(50,50,900,700);
+
+ JMenuBar menuBar = new JMenuBar();
+
+ JMenu fileMenu = new JMenu("File");
+
+
+ JMenuItem item3 = new JMenuItem("Connect");
+ item3.setEnabled(false);
+ fileMenu.add(item3);
+
+ JMenuItem item2 = new JMenuItem("Preferences");
+ item2.setEnabled(false);
+ fileMenu.add(item2);
+
+ fileMenu.addSeparator();
+
+ JMenuItem item = new JMenuItem();
+ item.setText("Quit");
+ item.addActionListener(new ActionListener() {
+ /**
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ System.exit(0);
+ }
+ });
+ fileMenu.add(item);
+
+ JMenu helpMenu = new JMenu("Help");
+ JMenuItem help = new JMenuItem();
+ help.setText("Help");
+ help.setEnabled(false);
+ helpMenu.add(help);
+
+ helpMenu.addSeparator();
+
+ JMenuItem itemAbout = new JMenuItem("About");
+ itemAbout.setEnabled(false);
+ helpMenu.add(itemAbout);
+
+ menuBar.add(fileMenu);
+ menuBar.add(helpMenu);
+ frame.setJMenuBar(menuBar);
+
+ HotelRoomTableModel model = new HotelRoomTableModel(hotelRoomManagerRemote);
+ SearchPanel searchPanel = new SearchPanel(model);
+
+ frame.getContentPane().add(searchPanel.getJComponent(), BorderLayout.NORTH);
+
+ JPanel southSide = new JPanel();
+ southSide.setLayout(new BoxLayout(southSide, BoxLayout.LINE_AXIS));
+
+ JButton searchButton = new JButton("Search [F1]");
+ JButton addButton = new JButton("Add [F2]");
+ JButton delButton = new JButton("Del [F3]");
+ JButton editButton = new JButton("Edit [F4]");
+ JButton bookButton = new JButton("Book [F5]");
+ JButton exportButton = new JButton("Export [F6]");
+ JButton helpButton = new JButton("Next [F7]");
+ JButton printButton = new JButton("Print [F8]");
+
+ searchButton.setEnabled(false);
+ addButton.setEnabled(false);
+ delButton.setEnabled(false);
+ editButton.setEnabled(false);
+ bookButton.setEnabled(false);
+ exportButton.setEnabled(false);
+ helpButton.setEnabled(false);
+ printButton.setEnabled(false);
+
+ southSide.add(searchButton);
+ southSide.add(addButton);
+ southSide.add(delButton);
+ southSide.add(editButton);
+ southSide.add(bookButton);
+ southSide.add(exportButton);
+ southSide.add(helpButton);
+ southSide.add(printButton);
+
+ frame.getContentPane().add(southSide, BorderLayout.SOUTH);
+
+ // center
+
+ final JTable table = new JTable(model);
+ table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+ TableRowSorter sorter = new TableRowSorter(model);
+ table.setRowSorter(sorter);
+
+ table.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent event) {
+ int viewRow = table.getSelectedRow();
+ if (viewRow < 0) {
+ //Selection got filtered away.
+ //statusText.setText("");
+ } else {
+ System.out.println("Selected; "+viewRow+" ... ");
+ //details.updateHotelRoom(model.getHotelRoom(viewRow));
+ }
+ }
+ });
+
+ JScrollPane mainPanel = new JScrollPane();
+ mainPanel.setWheelScrollingEnabled(true);
+ mainPanel.setViewportView(table);
+ mainPanel.getVerticalScrollBar().setUnitIncrement(10);
+ mainPanel.getHorizontalScrollBar().setUnitIncrement(10);
+ frame.getContentPane().add(mainPanel,BorderLayout.CENTER);
+
+ }
+
+ public void openView() {
+ frame.setVisible(true);
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/client/SearchPanel.java b/src/suncertify/client/SearchPanel.java
new file mode 100644
index 0000000..48f5b83
--- /dev/null
+++ b/src/suncertify/client/SearchPanel.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+
+package suncertify.client;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.Spring;
+import javax.swing.SpringLayout;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.PlainDocument;
+
+import suncertify.models.HotelRoom;
+
+/**
+ *
+ * @author willemc
+ *
+ */
+public class SearchPanel implements ActionListener,DocumentListener {
+
+ private JPanel searchPanel = null;
+ private JButton searchButton = null;
+ private JButton clearButton = null;
+ private HotelRoomTableModel model = null;
+ private JTextField f0;
+ private JTextField f1;
+ private JTextField f2;
+ private JTextField f3;
+
+ public SearchPanel(HotelRoomTableModel model) {
+ this.model=model;
+ searchPanel = new JPanel();
+ searchPanel.setLayout(new SpringLayout());
+ TitledBorder titledBorder = BorderFactory.createTitledBorder("Search");
+ titledBorder.setTitleJustification(TitledBorder.LEFT);
+ titledBorder.setTitlePosition(TitledBorder.TOP);
+ searchPanel.setBorder(titledBorder);
+ createPanel();
+ }
+
+ public JComponent getJComponent() {
+ return searchPanel;
+ }
+
+
+ private void createPanel() {
+
+ JPanel leftPanel = searchPanel;
+
+ JLabel l0 = new JLabel();
+ l0.setHorizontalAlignment(JLabel.TRAILING);
+ l0.setText("Custumber Number:");
+ leftPanel.add(l0);
+
+ f0 = new JTextField(15);
+ f0.getDocument().addDocumentListener(this);
+ leftPanel.add(f0);
+
+ JLabel l1 = new JLabel();
+ l1.setHorizontalAlignment(JLabel.TRAILING);
+ l1.setText("Name:");
+ leftPanel.add(l1);
+
+ f1 = new JTextField(15);
+ f1.getDocument().addDocumentListener(this);
+ leftPanel.add(f1);
+
+ JLabel l2 = new JLabel();
+ l2.setHorizontalAlignment(JLabel.TRAILING);
+ l2.setText("Location:");
+ leftPanel.add(l2);
+
+ f2 = new JTextField(15);
+ f2.getDocument().addDocumentListener(this);
+ leftPanel.add(f2);
+
+ //searchPanel.add(leftPanel,BorderLayout.LINE_START);
+
+ // === Center
+
+ JPanel centerPanel = searchPanel;
+
+ JLabel l3 = new JLabel();
+ l3.setHorizontalAlignment(JLabel.TRAILING);
+ l3.setText("Size:");
+ centerPanel.add(l3);
+
+ class IntTextDocument extends PlainDocument {
+ private static final long serialVersionUID = 7151907649485446049L;
+
+ public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
+ if (str == null) {
+ return;
+ }
+ String oldString = getText(0, getLength());
+ String newString = oldString.substring(0, offs) + str + oldString.substring(offs);
+ try {
+ Integer.parseInt(newString + "0");
+ super.insertString(offs, str, a);
+ } catch (NumberFormatException e) {
+ }
+ }
+ }
+
+ f3 = new JTextField(new IntTextDocument(),null,15);
+ f3.getDocument().addDocumentListener(this);
+ centerPanel.add(f3);
+
+
+ JLabel l4 = new JLabel();
+ l4.setHorizontalAlignment(JLabel.TRAILING);
+ l4.setText("Smoking:");
+ centerPanel.add(l4);
+
+ JCheckBox c1 = new JCheckBox();
+ centerPanel.add(c1);
+
+ JLabel l5 = new JLabel();
+ l5.setHorizontalAlignment(JLabel.TRAILING);
+ l5.setText("Price:");
+ centerPanel.add(l5);
+
+ //JTextField f4 = new JTextField(15);
+ //f4.getDocument().addDocumentListener(this);
+
+ String labels[] = { "10", "20", "50", "100","150", "200", "300", "400","500", "800" };
+ JComboBox comboBox = new JComboBox(labels);
+ comboBox.setMaximumRowCount(5);
+ comboBox.setEditable(true);
+
+ centerPanel.add(comboBox);
+
+
+ JLabel l6 = new JLabel();
+ l6.setHorizontalAlignment(JLabel.TRAILING);
+ l6.setText("Date:");
+ centerPanel.add(l6);
+
+ JTextField f5 = new JTextField(15);
+ //f4.getDocument().addDocumentListener(this);
+ centerPanel.add(f5);
+
+ //searchPanel.add(centerPanel,BorderLayout.CENTER);
+
+
+ // South
+
+ JLabel l7 = new JLabel();
+ centerPanel.add(l7);
+ JLabel l8 = new JLabel();
+ centerPanel.add(l8);
+
+ JPanel buttonPane = searchPanel;
+ //buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
+ //buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
+ //buttonPane.add(Box.createHorizontalGlue());
+
+
+ searchButton = new JButton();
+ searchButton.setText("Search");
+ searchButton.addActionListener(this);
+
+ buttonPane.add(searchButton);
+ //buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+
+ clearButton = new JButton();
+ clearButton.setText("Clear");
+ clearButton.addActionListener(this);
+
+ buttonPane.add(clearButton);
+
+ //searchPanel.add(buttonPane,BorderLayout.SOUTH);
+
+ //Layout the panel.
+ makeCompactGrid(searchPanel,
+ 3, 6, //rows, cols
+ 6, 6, //initX, initY
+ 6, 6); //xPad, yPad
+ }
+
+ private void updateSearch() {
+ HotelRoom room = new HotelRoom();
+ room.setName(f1.getText());
+ room.setLocation(f2.getText());
+
+ model.updateByCriteria(room);
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getSource()==searchButton) {
+ updateSearch();
+ }
+ if (e.getSource()==clearButton) {
+ f0.setText(null);
+ f1.setText(null);
+ f2.setText(null);
+ f3.setText(null);
+ updateSearch();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see javax.swing.event.DocumentListener#changedUpdate(javax.swing.event.DocumentEvent)
+ */
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ //updateSearch();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.swing.event.DocumentListener#insertUpdate(javax.swing.event.DocumentEvent)
+ */
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ updateSearch();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.swing.event.DocumentListener#removeUpdate(javax.swing.event.DocumentEvent)
+ */
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ updateSearch();
+ }
+
+
+ public static void makeCompactGrid(Container parent,
+ int rows, int cols,
+ int initialX, int initialY,
+ int xPad, int yPad) {
+ SpringLayout layout;
+ try {
+ layout = (SpringLayout)parent.getLayout();
+ } catch (ClassCastException exc) {
+ System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
+ return;
+ }
+
+ //Align all cells in each column and make them the same width.
+ Spring x = Spring.constant(initialX);
+ for (int c = 0; c < cols; c++) {
+ Spring width = Spring.constant(0);
+ for (int r = 0; r < rows; r++) {
+ width = Spring.max(width,getConstraintsForCell(r, c, parent, cols).getWidth());
+ }
+ for (int r = 0; r < rows; r++) {
+ SpringLayout.Constraints constraints =
+ getConstraintsForCell(r, c, parent, cols);
+ constraints.setX(x);
+ constraints.setWidth(width);
+ }
+ x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad)));
+ }
+
+ //Align all cells in each row and make them the same height.
+ Spring y = Spring.constant(initialY);
+ for (int r = 0; r < rows; r++) {
+ Spring height = Spring.constant(0);
+ for (int c = 0; c < cols; c++) {
+ height = Spring.max(height,getConstraintsForCell(r, c, parent, cols).getHeight());
+ }
+ for (int c = 0; c < cols; c++) {
+ SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols);
+ constraints.setY(y);
+ constraints.setHeight(height);
+ }
+ y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad)));
+ }
+
+ //Set the parent's size.
+ SpringLayout.Constraints pCons = layout.getConstraints(parent);
+ pCons.setConstraint(SpringLayout.SOUTH, y);
+ pCons.setConstraint(SpringLayout.EAST, x);
+ }
+
+ private static SpringLayout.Constraints getConstraintsForCell(
+ int row, int col,
+ Container parent,
+ int cols) {
+ SpringLayout layout = (SpringLayout) parent.getLayout();
+ Component c = parent.getComponent(row * cols + col);
+ return layout.getConstraints(c);
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/client/package.html b/src/suncertify/client/package.html
new file mode 100644
index 0000000..b52847d
--- /dev/null
+++ b/src/suncertify/client/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+The GUI of the application.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/core/LoadHotelRoomDB.java b/src/suncertify/core/LoadHotelRoomDB.java
new file mode 100644
index 0000000..28fef50
--- /dev/null
+++ b/src/suncertify/core/LoadHotelRoomDB.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.core;
+
+import java.io.File;
+import java.io.IOException;
+
+import suncertify.db.DataBaseManager;
+import suncertify.db.data.Table;
+import suncertify.db.data.column.BooleanObjectConverter;
+import suncertify.db.data.column.Column;
+import suncertify.db.data.column.DateObjectConverter;
+import suncertify.db.data.column.IntegerObjectConverter;
+import suncertify.db.data.column.LongObjectConverter;
+import suncertify.db.data.column.StringColumnSearcher;
+import suncertify.db.data.column.StringObjectConverter;
+
+
+/**
+ * LoadHotelRoomDB loads the db-1x3.db database file.
+ * And fills in some meta data in the Table model which is missing in the db file.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class LoadHotelRoomDB {
+
+
+ /**
+ * Loads the database and configs the fields acouring the file specefications.
+ * @param dbm
+ * @return
+ * @throws IOException
+ */
+ public Table loadTable(DataBaseManager dbm) throws IOException {
+
+ File f = new File("db-1x3.db");
+ dbm.openTable(f);
+
+ Table table = dbm.getTable(f.getName());
+ // manual config for data safe type
+ for (Column c:table.getColumns()) {
+ if (1==c.getColumnIndex() | 2==c.getColumnIndex()) {
+ c.setObjectConverter(new StringObjectConverter());
+ c.setColumnSearcher(new StringColumnSearcher());
+ }
+ if (3==c.getColumnIndex() | 7==c.getColumnIndex()) {
+ c.setObjectConverter(new IntegerObjectConverter());
+ }
+ if (4==c.getColumnIndex()) {
+ c.setObjectConverter(new BooleanObjectConverter());
+ }
+ if (5==c.getColumnIndex()) {
+ c.setObjectConverter(new LongObjectConverter());
+ }
+ if (6==c.getColumnIndex()) {
+ c.setObjectConverter(new DateObjectConverter());
+ }
+ }
+ return table;
+ }
+
+}
\ No newline at end of file
diff --git a/src/suncertify/core/LoadServerBeans.java b/src/suncertify/core/LoadServerBeans.java
new file mode 100644
index 0000000..e12b5de
--- /dev/null
+++ b/src/suncertify/core/LoadServerBeans.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.core;
+
+import java.io.IOException;
+
+import suncertify.db.DataBaseManager;
+import suncertify.models.HotelRoomDBConverter;
+import suncertify.server.ServerManager;
+import suncertify.server.beans.BookingLogManager;
+import suncertify.server.beans.BookingLogManagerRemote;
+import suncertify.server.beans.HotelRoomManager;
+import suncertify.server.beans.HotelRoomManagerRemote;
+
+
+/**
+ * Loads the application needed beans into the ServerManager
+ *
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class LoadServerBeans {
+
+ /**
+ * Loads the application backend beans into the ServerManager
+ * @param server The server to initation with beans.
+ * @param dataBaseManager The databaseManager is needed for injection into beans.
+ * @throws IOException
+ */
+ public void loadBeans(ServerManager server,DataBaseManager dataBaseManager) throws IOException {
+
+ server.putServerInitBean("hotelRoom.tableBackend", dataBaseManager.getHotelRoomDB());
+ server.putServerInitBean("hotelRoom.beanConverter", new HotelRoomDBConverter(dataBaseManager.getHotelRoomTable()));
+
+ server.putServerBean(HotelRoomManagerRemote.class,HotelRoomManager.class);
+ server.putServerBean(BookingLogManagerRemote.class,BookingLogManager.class);
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/core/NetworkServerWorker.java b/src/suncertify/core/NetworkServerWorker.java
new file mode 100644
index 0000000..ed4d080
--- /dev/null
+++ b/src/suncertify/core/NetworkServerWorker.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+
+package suncertify.core;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.lang.reflect.Method;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import suncertify.net.NetworkRequest;
+import suncertify.net.NetworkResponse;
+import suncertify.net.NetworkServer;
+import suncertify.net.NetworkNIOConnector.ObjectHandler;
+import suncertify.server.ServerManager;
+
+/**
+ * The NetworkServerWorker
+ *
+ * This Runnable worker handlers the data from the nio server and decode it.
+ * Then it will invoke the bean methode and return the result to the nio server.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+
+public class NetworkServerWorker implements Runnable {
+
+ private Logger logger = Logger.getLogger(NetworkServerWorker.class.getName());
+ NetworkServer server = null;
+ SocketChannel socket = null;
+ private ServerManager serverManager = null;
+ private ByteBuffer data = ByteBuffer.allocate(8192);
+
+ /**
+ * Creates an new NetworkServerWorker which can handle requests from the server and executes them on the serverManager.
+ * @param serverManager
+ * @param server
+ * @param socket
+ * @param data
+ */
+ public NetworkServerWorker(ServerManager serverManager,NetworkServer server,SocketChannel socket,byte[] data) {
+ this.socket=socket;
+ this.server=server;
+ this.data.put(data);
+ this.data.limit(data.length);
+ this.serverManager=serverManager;
+ }
+
+ /**
+ * Gets an chuck of data from server and process an response to it.
+ */
+ public void run() {
+ String tName = Thread.currentThread().getName();
+ long startTime = System.currentTimeMillis();
+ logger.fine("Starting exec in "+tName);
+ try {
+ ByteBuffer leftOver = server.receiveObject(socket,data, new ObjectHandler() {
+ public void processObject(Object object) {
+ NetworkRequest request = (NetworkRequest)object;
+ NetworkResponse response = new NetworkResponse(request.getRequestId());
+ try {
+ Object bean = serverManager.getServerBean(request.getBeanName());
+
+ int pS = 0;
+ if (request.getMethodArgs()!=null) {
+ pS = request.getMethodArgs().length;
+ }
+ Class>[] para = new Class[pS];
+ for (int i=0;i1) {
+ System.err.println("To many arguments given, only one optionale argument supperted with values: "+StartupMode.values());
+ System.exit(1);
+ }
+ SunCertifyStarter starter = new SunCertifyStarter(mode);
+ try {
+ starter.start();
+ } catch (Exception e) {
+ System.err.println("Could not start SunCertify: "+e.getMessage());
+ System.exit(1);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/core/package.html b/src/suncertify/core/package.html
new file mode 100644
index 0000000..9592f01
--- /dev/null
+++ b/src/suncertify/core/package.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+The core classes.
+These bind the different object layers of the application toghter to form the application.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/db/DB.java b/src/suncertify/db/DB.java
new file mode 100644
index 0000000..f6429b8
--- /dev/null
+++ b/src/suncertify/db/DB.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db;
+
+/**
+ * File provided by the Application Submission
+ *
+ * @author suncertify.db
+ * @version 1.0 Dec 14, 2008
+ */
+public interface DB {
+
+ /**
+ * Reads a record from the file. Returns an array where each
+ * element is a record value.
+ */
+ public String[] read(int recNo) throws RecordNotFoundException;
+
+ /**
+ * Modifies the fields of a record. The new value for field n
+ * appears in data[n]. Throws SecurityException
+ * if the record is locked with a cookie other than lockCookie.
+ */
+ public void update(int recNo, String[] data, long lockCookie) throws RecordNotFoundException, SecurityException;
+
+ /**
+ * Deletes a record, making the record number and associated disk
+ * storage available for reuse.
+ * Throws SecurityException if the record is locked with a cookie
+ * other than lockCookie.
+ *
+ * @param recNo
+ * @param lockCookie
+ * @throws RecordNotFoundException
+ * @throws SecurityException
+ */
+ public void delete(int recNo, long lockCookie) throws RecordNotFoundException, SecurityException;
+
+ /**
+ *
+ * Returns an array of record numbers that match the specified
+ * criteria. Field n in the database file is described by
+ * criteria[n]. A null value in criteria[n] matches any field
+ * value. A non-null value in criteria[n] matches any field
+ * value that begins with criteria[n]. (For example, "Fred"
+ * matches "Fred" or "Freddy".)
+ *
+ * @param criteria
+ * @return
+ */
+ public int[] find(String[] criteria);
+
+ /**
+ * Creates a new record in the database (possibly reusing a
+ * deleted entry). Inserts the given data, and returns the record
+ * number of the new record.
+ *
+ * @param data
+ * @return
+ * @throws DuplicateKeyException
+ */
+ public int create(String[] data) throws DuplicateKeyException;
+
+ /**
+ * Locks a record so that it can only be updated or deleted by this client.
+ * Returned value is a cookie that must be used when the record is unlocked,
+ * updated, or deleted. If the specified record is already locked by a different
+ * client, the current thread gives up the CPU and consumes no CPU cycles until
+ * the record is unlocked.
+ *
+ * @param recNo
+ * @return
+ * @throws RecordNotFoundException
+ */
+ public long lock(int recNo) throws RecordNotFoundException;
+
+ /**
+ * Releases the lock on a record. Cookie must be the cookie
+ * returned when the record was locked; otherwise throws SecurityException.
+ *
+ * @param recNo
+ * @param cookie
+ * @throws RecordNotFoundException
+ * @throws SecurityException
+ */
+ public void unlock(int recNo, long cookie) throws RecordNotFoundException, SecurityException;
+}
diff --git a/src/suncertify/db/Data.java b/src/suncertify/db/Data.java
new file mode 100644
index 0000000..7ca6f32
--- /dev/null
+++ b/src/suncertify/db/Data.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db;
+
+import suncertify.db.data.Table;
+
+/**
+ * Wrapper class for interface DB , to Table object.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class Data implements DB {
+
+ /** The table we wrap. */
+ final private Table table;
+
+ /**
+ * Creates an Data wrapper for an Table object.
+ * @param table
+ */
+ public Data(Table table) {
+ this.table=table;
+ }
+
+ /**
+ * @see suncertify.db.DB#read(int)
+ */
+ @Override
+ public String[] read(int row) throws RecordNotFoundException {
+ return table.getInterfaceRow(row);
+ }
+
+
+ /**
+ * @see suncertify.db.DB#find(java.lang.String[])
+ */
+ @Override
+ public int[] find(String[] criteria) {
+ return table.searchByInterfaceCriteria(criteria);
+ }
+
+ /**
+ * @see suncertify.db.DB#lock(int)
+ */
+ @Override
+ public long lock(int row) throws RecordNotFoundException {
+ return table.lock(row).getLockId();
+ }
+
+ /**
+ * @see suncertify.db.DB#unlock(int, long)
+ */
+ @Override
+ public void unlock(int row, long lockId) throws RecordNotFoundException,SecurityException {
+ table.unlock(row, lockId);
+ }
+
+ /**
+ * @see suncertify.db.DB#update(int, java.lang.String[], long)
+ */
+ @Override
+ public void update(int row, String[] data, long lockId) throws RecordNotFoundException, SecurityException {
+ table.update(row, data, lockId);
+ }
+
+ /**
+ * @see suncertify.db.DB#delete(int, long)
+ */
+ @Override
+ public void delete(int row, long lockId) throws RecordNotFoundException, SecurityException {
+ table.delete(row, lockId);
+ }
+
+ /**
+ * @see suncertify.db.DB#create(java.lang.String[])
+ */
+ @Override
+ public int create(String[] data) throws DuplicateKeyException {
+ return table.create(data);
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/db/DataBaseManager.java b/src/suncertify/db/DataBaseManager.java
new file mode 100644
index 0000000..86c1254
--- /dev/null
+++ b/src/suncertify/db/DataBaseManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import suncertify.db.data.Table;
+
+/**
+ * Manages all open database files in the application.
+ *
+ * At the moment this is only one.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class DataBaseManager {
+
+ private Logger logger = Logger.getLogger(DataBaseManager.class.getName());
+ private Map tables = null;
+
+ /**
+ * Creates the DataBaseManager.
+ */
+ public DataBaseManager() {
+ tables = new HashMap(5);
+ }
+
+ /**
+ * @return the only managed database table namely the hotelRoom table.
+ */
+ public DB getHotelRoomDB() {
+ Table table = getTable("db-1x3.db");
+ DB db = new Data(table);
+ return db;
+ }
+
+ // hackje voor server->DB-api converters
+ public Table getHotelRoomTable() {
+ return getTable("db-1x3.db");
+ }
+
+ /**
+ * Starts the database
+ */
+ public void start() {
+ }
+
+ /**
+ * Stops the database manager
+ */
+ public void stop() {
+ for (Table t:tables.values()) {
+ try {
+ closeTable(t);
+ } catch (Exception e) {
+ logger.log(Level.WARNING,"Error while closing table: "+t.getName()+" error: "+e.getMessage(),e);
+ }
+ }
+ }
+
+ public Table openTable(File file) throws IOException {
+ Table t = new Table();
+ t.openTable(file);
+ tables.put(t.getName(),t);
+ logger.fine("Opened table: "+t.getName());
+ return t;
+ }
+
+ public void closeTable(Table table) throws IOException {
+ logger.fine("Closing table: "+table.getName());
+ table.closeTable();
+ }
+
+ public Table getTable(String name) {
+ return tables.get(name);
+ }
+
+}
\ No newline at end of file
diff --git a/src/suncertify/db/DuplicateKeyException.java b/src/suncertify/db/DuplicateKeyException.java
new file mode 100644
index 0000000..502c957
--- /dev/null
+++ b/src/suncertify/db/DuplicateKeyException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db;
+
+/**
+ * An DuplicateKeyException is thrown the the database when a record already execits in the database.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+@SuppressWarnings("serial")
+public class DuplicateKeyException extends Exception {
+
+ /**
+ * Default constructor
+ */
+ public DuplicateKeyException() {
+ }
+
+ /**
+ * Message constructor
+ * @param message The error message
+ */
+ public DuplicateKeyException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/suncertify/db/RecordNotFoundException.java b/src/suncertify/db/RecordNotFoundException.java
new file mode 100644
index 0000000..8e8407a
--- /dev/null
+++ b/src/suncertify/db/RecordNotFoundException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db;
+
+/**
+ * An RecordNotFoundException is thrown the the database when a record is not found.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+@SuppressWarnings("serial")
+public class RecordNotFoundException extends Exception {
+
+ /**
+ * Default constructor
+ */
+ public RecordNotFoundException() {
+ }
+
+ /**
+ * Message constructor
+ * @param message The error message
+ */
+ public RecordNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/src/suncertify/db/data/RowCache.java b/src/suncertify/db/data/RowCache.java
new file mode 100644
index 0000000..f5cf912
--- /dev/null
+++ b/src/suncertify/db/data/RowCache.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data;
+
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * RowCache stores one record and meta data of data for caching.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class RowCache {
+
+ private Integer recordRow = null;
+ private String[] record = null;
+ private Date dateCreated = null;
+ private Date dateAccess = null;
+ private AtomicInteger countAccess = null;
+
+ /**
+ * Creates new cache row and validates it contains data.
+ * @param record
+ */
+ public RowCache(Integer recordRow,String[] record) {
+ if (recordRow==null) {
+ throw new NullPointerException("Can't cache null record row number.");
+ }
+ if (record==null) {
+ throw new NullPointerException("Can't cache null record.");
+ }
+ if (record.length==0) {
+ throw new NullPointerException("Can't cache empty record.");
+ }
+ this.record=record;
+ dateCreated = new Date();
+ dateAccess = new Date();
+ countAccess = new AtomicInteger(0);
+ }
+
+ /**
+ * @return the record
+ */
+ public String[] getRecord() {
+ dateAccess = new Date();
+ countAccess.incrementAndGet();
+ return record;
+ }
+
+ /**
+ * @return the dateCreated
+ */
+ public Date getDateCreated() {
+ return dateCreated;
+ }
+
+ /**
+ * @return the dateAccess
+ */
+ public Date getDateAccess() {
+ return dateAccess;
+ }
+
+ /**
+ * note: use int here so we not cast to much.
+ * @return the countAccess
+ */
+ public int getCountAccess() {
+ return countAccess.get();
+ }
+
+ /**
+ * @return the recordRow
+ */
+ public Integer getRecordRow() {
+ return recordRow;
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/db/data/RowLock.java b/src/suncertify/db/data/RowLock.java
new file mode 100644
index 0000000..ed1a14d
--- /dev/null
+++ b/src/suncertify/db/data/RowLock.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data;
+
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * RowLock lock a certain row and has timeout of the lock.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class RowLock {
+
+ private Integer recordRow = null;
+ private Long lockId = null;
+ private Date dateCreated = null;
+ private Date dateAccess = null;
+ private Date dateRelease = null;
+ private AtomicInteger countAccess = null;
+
+ /**
+ * Creates an RowLock
+ * @param recordRow The row index to lock.
+ * @param lockTimeout The lock timeout
+ * @param lockId The id of the lock
+ */
+ public RowLock(Integer recordRow,long lockTimeout,long lockId) {
+ if (recordRow==null) {
+ throw new NullPointerException("Can't lock null record row number.");
+ }
+ dateCreated = new Date();
+ dateAccess = new Date();
+ dateRelease = new Date(dateAccess.getTime()+lockTimeout);
+ countAccess = new AtomicInteger(0);
+ this.lockId = lockId;
+ }
+
+ /**
+ * @return the recordRow
+ */
+ public Integer getRecordRow() {
+ return recordRow;
+ }
+
+ /**
+ * @return the dateCreated
+ */
+ public Date getDateCreated() {
+ return dateCreated;
+ }
+
+ /**
+ * @return the dateAccess
+ */
+ public Date getDateAccess() {
+ return dateAccess;
+ }
+
+ /**
+ * @return the dateRelease
+ */
+ public Date getDateRelease() {
+ return dateRelease;
+ }
+
+ /**
+ * note: use int here so we not cast to much.
+ * @return the countAccess
+ */
+ public int getCountAccess() {
+ return countAccess.get();
+ }
+
+ /**
+ * @return the lockId
+ */
+ public Long getLockId() {
+ return lockId;
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/db/data/Table.java b/src/suncertify/db/data/Table.java
new file mode 100644
index 0000000..7bee91b
--- /dev/null
+++ b/src/suncertify/db/data/Table.java
@@ -0,0 +1,767 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.lang.reflect.Method;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import suncertify.db.DuplicateKeyException;
+import suncertify.db.RecordNotFoundException;
+import suncertify.db.data.column.Column;
+import suncertify.db.data.column.DeleteFlagConverter;
+
+/**
+ * The Table class manages one open database file on disk.
+ *
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class Table {
+
+ /** The NMAP'ed bytebuffer to the file database on disk. */
+ private MappedByteBuffer byteBuffer = null;
+ /** Object to locking the bytebuffer. */
+ private Object byteBufferLock = new Object();
+ /** All the columns of this table */
+ private List columns = null;
+ /** The logger to log to. */
+ private Logger logger = null;
+ /** The total records which are in this file.(incl deleted) */
+ private int records = 0;
+ /** Offset where the data starts in the byteBuffer */
+ private int recordsOffset = 0;
+ /** The charset encoding used for the data. */
+ private Charset charset = null;
+ /** The name of the file */
+ private String name = null;
+ /** locks for rows */
+ private Map locks = Collections.synchronizedMap(new HashMap());
+ /** cach for rows */
+ private Map cache = new HashMap();
+ /** The maximum of rows in the cache. */
+ private int cacheMax = 100;
+ /** File which is opened */
+ private RandomAccessFile tableFile = null;
+ /** The number of records added when table is enlarged. */
+ private int extraRecords = 4;
+
+ // declair final so can't be altered by reflection
+ /** The readLock for the cache */
+ final private Lock readLock;
+ /** The writeLock for the cache */
+ final private Lock writeLock;
+ /** The row lock counter */
+ final private AtomicInteger rowLocks;
+
+ // REST ARE cache values
+
+ private int totalColumnsByteSize = 0;
+
+ private boolean isFinerLog = false;
+
+ /**
+ * Creates the table object.
+ */
+ public Table() {
+ logger = Logger.getLogger(Table.class.getName());
+ isFinerLog = logger.isLoggable(Level.FINER);
+ columns = new ArrayList(10);
+ charset = Charset.forName("US-ASCII");
+
+ ReentrantReadWriteLock locking = new ReentrantReadWriteLock();
+ readLock = locking.readLock();
+ writeLock = locking.writeLock();
+
+ Random random = new Random();
+ rowLocks= new AtomicInteger(random.nextInt());
+ }
+
+ /**
+ * Opens a table.
+ *
+ * note: no locking here because connurent access is pas possible
+ * when the table is put in DBManager table storage, which is after this method call.
+ *
+ * @param file
+ */
+ public void openTable(File file) throws IOException {
+ // http://www.codefund.com/52/nio-mappedbytebuffer-filechannel-resizing-524047.shtm
+ //
+ long startTime = System.currentTimeMillis();
+ name = file.getName();
+
+ tableFile = new RandomAccessFile(file,"rw");
+ FileChannel channel = tableFile.getChannel();
+ int length = (int)channel.size();
+ logger.fine("Mapping table size: "+length);
+ byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
+ logger.finer("Buf info direct: "+byteBuffer.isDirect()+" loaded: "+byteBuffer.isLoaded()+" readonly: "+byteBuffer.isReadOnly());
+ // read 4 magic bytes
+ int cookie = byteBuffer.getInt();
+ logger.fine("DB File cookie: "+cookie);
+ if (259!=cookie) {
+ throw new IllegalStateException("DB file magic cookie is not correct 259!="+cookie);
+ }
+
+ // # fields
+ char fields = byteBuffer.getChar();
+
+ // dele flag
+ Column c = new Column();
+ c.setCharset(charset);
+ c.setFieldLength(1);
+ c.setFieldName("___DB___DELETED_ROW_FLAG");
+ c.setFieldNameLength(c.getFieldName().length()); // should column 0 should not be saved.
+ c.setObjectConverter(new DeleteFlagConverter());
+ c.setColumnIndex(0);
+ addColumn(c);
+
+ // header
+ for (int i=0;i columns) throws IOException {
+
+ if (file.exists()) {
+ throw new IllegalArgumentException("File: "+file.getName()+" already excists.");
+ }
+ if (columns.size()>255) {
+ throw new IllegalArgumentException("More then 255 columns is not supported.");
+ }
+ char colSize = (char)columns.size();
+
+ // create file
+ FileOutputStream outputFile = new FileOutputStream(file, true);
+ logger.info("File stream created successfully: "+file.getName());
+
+ try {
+ long startTime = System.currentTimeMillis();
+
+ FileChannel channel = outputFile.getChannel();
+ MappedByteBuffer byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 4096);
+ logger.finer("Buf info direct: "+byteBuffer.isDirect()+" loaded: "+byteBuffer.isLoaded()+" readonly: "+byteBuffer.isReadOnly());
+
+ // write 4 magic bytes
+ byteBuffer.putInt(259);
+
+ // # fields
+ byteBuffer.putChar(colSize);
+
+ // write field headers
+ for (int i=0;i0) {
+ return totalColumnsByteSize;
+ }
+ int result = 0;
+ for (Column c:columns) {
+ result+=c.getFieldLength();
+ }
+ totalColumnsByteSize = result;
+ return result;
+ }
+
+ /**
+ * Checks if the row Id is in bound of this table.
+ * @param row The row to check
+ * @throws RecordNotFoundException Throws an RecordNotFoundException if row is negative or is out of bounds.
+ */
+ private void checkBounds(int row) throws RecordNotFoundException {
+ if (row<0) {
+ throw new RecordNotFoundException("Can't handle negative rows.");
+ }
+ if (row>records) {
+ throw new RecordNotFoundException("row number is out of bounds.");
+ }
+ }
+
+ /**
+ * Check if this row is an deleted row.
+ * @param row The row to check.
+ * @return true if row is an deleted row.
+ */
+ private boolean checkDeletedRow(int row) {
+ Column c = columns.get(0);
+ byte[] data = getColumnDataByte(row,0);
+ // extra converting to fit in db interface
+ String result = c.getObjectConverter().decodeStorage(c, data);
+ if ("true".equals(result)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if an row is deleted.
+ * @param row The row to check.
+ * @throws RecordNotFoundException is throw when the row is deleted.
+ */
+ private void checkDeleted(int row) throws RecordNotFoundException {
+ if (checkDeletedRow(row)) {
+ throw new RecordNotFoundException("row is deleted: "+row);
+ }
+ }
+
+ private Map colBytes = new HashMap(20);
+ public int getTotalColumnsByteSize(int col) {
+
+ // cached value.
+ if (colBytes.containsKey(col)) {
+ return colBytes.get(col);
+ }
+
+ int result = 0;
+ int cols = 0;
+ for (Column c:columns) {
+ // we store the number of bytes BEFORE column !!
+ colBytes.put(cols, result);
+ result+=c.getFieldLength();
+ cols++;
+ }
+
+ if (colBytes.containsKey(col)) {
+ return colBytes.get(col);
+ }
+
+ throw new IllegalArgumentException("Column not found: "+col);
+ }
+
+ private byte[] getColumnDataByte(int row,int column) {
+ Column c = columns.get(column);
+ int bb = getTotalColumnsByteSize();
+ int offset = recordsOffset+(row*bb)+getTotalColumnsByteSize(column);
+ if (isFinerLog) {
+ logger.finer("Get raw data byte[] ACCESS: row: "+row+" col: "+column+" off: "+offset+" recs: "+records+" start: "+recordsOffset+" recLen: "+bb);
+ }
+ byte[] result = new byte[c.getFieldLength()];
+
+ // only lock byteBuffer access
+ synchronized (byteBufferLock) {
+ byteBuffer.position(offset);
+ byteBuffer.get(result, 0, c.getFieldLength());
+ }
+ return result;
+
+ }
+
+ private void setColumnDataByte(int row,int column,byte[] data) {
+ int bb = getTotalColumnsByteSize();
+ int offset = recordsOffset+(row*bb)+getTotalColumnsByteSize(column);
+ if (isFinerLog) {
+ logger.finer("Set raw data byte[] ACCESS: row: "+row+" col: "+column+" off: "+offset+" recs: "+records+" start: "+recordsOffset+" recLen: "+bb+" dataSize: "+data.length);
+ }
+ if (data.length>bb) {
+ throw new SecurityException("May not writes longer data then ");
+ }
+ if (offset>=byteBuffer.limit()) {
+ resizeTable();
+ }
+
+ // only lock byteBuffer access
+ synchronized (byteBufferLock) {
+ byteBuffer.position(offset);
+ byteBuffer.put(data, 0, data.length);
+ }
+ }
+
+ private String getColumnDataString(int row,int column) {
+ Column c = columns.get(column);
+ byte[] data = getColumnDataByte(row,column);
+
+ // extra converting to fit in db interface
+ String result = c.getObjectConverter().decodeStorage(c, data);
+ try {
+ Object o = c.getObjectConverter().decodeInterface(c, result);
+ String result2 = c.getObjectConverter().encodeInterface(c, o);
+ return result2;
+ } catch (Exception e) {
+ throw new IllegalStateException("Could not convert data: "+e.getMessage(),e);
+ }
+ }
+
+ private void putColumnDataString(int row,int column,String data) {
+ Column c = columns.get(column);
+ // extra converting to fit in db interface
+ Object result = c.getObjectConverter().decodeInterface(c, data);
+ try {
+ String o = c.getObjectConverter().encodeInterface(c, result);
+ byte[] result2 = c.getObjectConverter().encodeStorage(c, o);
+ setColumnDataByte(row,column,result2);
+ } catch (Exception e) {
+ throw new IllegalStateException("Could not convert data: "+e.getMessage(),e);
+ }
+ }
+
+ public String[] getInterfaceRow(int row) throws RecordNotFoundException {
+ String[] result = cacheGet(row);
+ if (result!=null) {
+ return result;
+ }
+
+ checkBounds(row);
+ checkDeleted(row);
+
+ int cols = getTotalColumns()-1;
+ result = new String[cols];
+ for (int i=0;i getColumns() {
+ return columns;
+ }
+
+ /**
+ * Returns the name of this table
+ * @return The name of this table
+ */
+ public String getName() {
+ return name;
+ }
+
+ // search
+
+ public int[] searchByInterfaceCriteria(String[] criteria) {
+
+ if (criteria==null) {
+ throw new NullPointerException("Can't search null criteria");
+ }
+ // -1 = deleted flag column
+ if (criteria.length!=getTotalColumns()-1) {
+ throw new IllegalArgumentException("criteria array is not same size as table columns.");
+ }
+
+ Set result = new HashSet(50);
+
+
+ for (int i=0;icacheMax) {
+ return; // don't put anything in cache until there is space
+ }
+ } finally {
+ readLock.unlock();
+ }
+ try {
+ writeLock.lock(); // write lock and add the new cache item
+ RowCache c = new RowCache(row,data);
+ cache.put(row, c);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+ /**
+ * Removes an cache item
+ * @param row
+ */
+ private void cacheRemove(int row) {
+ try {
+ writeLock.lock(); // write lock and removed the cache item.
+ cache.remove(row);
+ } finally {
+ writeLock.unlock();
+ }
+ }
+
+}
diff --git a/src/suncertify/db/data/column/AbstractColumnObjectConverter.java b/src/suncertify/db/data/column/AbstractColumnObjectConverter.java
new file mode 100644
index 0000000..ffdd7bc
--- /dev/null
+++ b/src/suncertify/db/data/column/AbstractColumnObjectConverter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+
+abstract public class AbstractColumnObjectConverter implements ColumnObjectConverter {
+
+ protected Class> convertClass = null;
+ abstract public Object decodeInterface(Column c,String object);
+ abstract public String encodeInterface(Column c,Object object);
+
+ public AbstractColumnObjectConverter(Class> convertClass) {
+ this.convertClass=convertClass;
+ }
+
+ /**
+ * @see suncertify.db.data.column.ColumnObjectConverter#getConvertClass()
+ */
+ @Override
+ public Class> getConvertClass() {
+ return convertClass;
+ }
+ /**
+ * @see suncertify.db.data.column.ColumnObjectConverter#decodeStorage(suncertify.db.data.column.Column, java.lang.Object)
+ */
+ @Override
+ public byte[] encodeStorage(Column c, String str) {
+ if (str.length()>c.getFieldLength()) {
+ throw new IllegalArgumentException("String is to large to DB");
+ }
+ byte[] data = str.getBytes(c.getCharset());
+
+ // fill out space
+ if (data.length getConvertClass();
+
+ public Object decodeInterface(Column c,String object);
+ public String encodeInterface(Column c,Object object);
+
+ public String decodeStorage(Column c,byte[] data);
+ public byte[] encodeStorage(Column c,String object);
+}
diff --git a/src/suncertify/db/data/column/ColumnSearcher.java b/src/suncertify/db/data/column/ColumnSearcher.java
new file mode 100644
index 0000000..453251b
--- /dev/null
+++ b/src/suncertify/db/data/column/ColumnSearcher.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+
+import suncertify.db.RecordNotFoundException;
+import suncertify.db.data.Table;
+
+
+public interface ColumnSearcher {
+
+ public boolean searchColumn(Table table, Column column,String searchString,String columnData) throws RecordNotFoundException;
+}
diff --git a/src/suncertify/db/data/column/DateObjectConverter.java b/src/suncertify/db/data/column/DateObjectConverter.java
new file mode 100644
index 0000000..8b52d32
--- /dev/null
+++ b/src/suncertify/db/data/column/DateObjectConverter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+
+public class DateObjectConverter extends AbstractColumnObjectConverter {
+
+ public DateObjectConverter() {
+ super(Date.class);
+ }
+
+ public Object decodeInterface(Column c,String object) {
+ Date realDate = null;
+ try {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
+ realDate = dateFormat.parse(object);
+ } catch (ParseException e) {
+ throw new IllegalStateException("Date format error on: '"+object+"'");
+ }
+ return realDate;
+ }
+
+ public String encodeInterface(Column c,Object object) {
+ if ((object instanceof Date)==false) {
+ throw new IllegalStateException("Object is no date");
+ }
+ String value = null;
+ DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
+ value = dateFormat.format((Date)object);
+ return value;
+ }
+}
diff --git a/src/suncertify/db/data/column/DeleteFlagConverter.java b/src/suncertify/db/data/column/DeleteFlagConverter.java
new file mode 100644
index 0000000..65e5796
--- /dev/null
+++ b/src/suncertify/db/data/column/DeleteFlagConverter.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+
+public class DeleteFlagConverter extends BooleanObjectConverter {
+
+ @Override
+ public byte[] encodeStorage(Column c, String str) {
+ Boolean value = new Boolean(str);
+ byte[] data = new byte[1];
+ if (value) {
+ data[0] = (byte)255; // java bytes are signed so; -128 == 255 == 0xFF
+ } else {
+ data[0] = 0;
+ }
+ return data;
+ }
+
+ @Override
+ public String decodeStorage(Column c, byte[] data) {
+ if (data[0]==0) {
+ return new Boolean(false).toString();
+ } else if (data[0]==(byte)255) {
+ return new Boolean(true).toString();
+ }
+ throw new IllegalArgumentException("Unknow delete flag value: '"+data[0]+"'");
+ }
+}
diff --git a/src/suncertify/db/data/column/IntegerObjectConverter.java b/src/suncertify/db/data/column/IntegerObjectConverter.java
new file mode 100644
index 0000000..1588f8f
--- /dev/null
+++ b/src/suncertify/db/data/column/IntegerObjectConverter.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+
+public class IntegerObjectConverter extends AbstractColumnObjectConverter {
+
+ public IntegerObjectConverter() {
+ super(Integer.class);
+ }
+
+ public Object decodeInterface(Column c,String object) {
+ if ("".equals(object)) {
+ return 0;
+ }
+ Integer result = new Integer(object);
+ return result;
+ }
+
+ public String encodeInterface(Column c,Object object) {
+ if ((object instanceof Integer)==false) {
+ throw new IllegalStateException("wrong data type");
+ }
+ Integer value = (Integer)object;
+ return value.toString();
+ }
+}
diff --git a/src/suncertify/db/data/column/LongObjectConverter.java b/src/suncertify/db/data/column/LongObjectConverter.java
new file mode 100644
index 0000000..be4f194
--- /dev/null
+++ b/src/suncertify/db/data/column/LongObjectConverter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+
+public class LongObjectConverter extends AbstractColumnObjectConverter {
+
+ public LongObjectConverter() {
+ super(Long.class);
+ }
+
+ public Object decodeInterface(Column c,String object) {
+ Long realRate = null;
+ try {
+ Number number = NumberFormat.getCurrencyInstance(Locale.getDefault()).parse(object);
+ // 123.45
+ if (number instanceof Long) {
+ // Long value
+ realRate = (Long)number;
+ } else if (number instanceof Double) {
+ realRate = ((Double)number).longValue();
+ } else {
+ System.out.println("unknow number: "+number.getClass());
+ }
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return realRate;
+ }
+
+ public String encodeInterface(Column c,Object object) {
+ if ((object instanceof Long)==false) {
+ throw new IllegalStateException("Data err");
+ }
+ Long value = (Long)object;
+ String str = "$"+value;
+ if (str.length()>c.getFieldLength()) {
+ return null;
+ }
+ return str;
+ }
+}
diff --git a/src/suncertify/db/data/column/StringColumnSearcher.java b/src/suncertify/db/data/column/StringColumnSearcher.java
new file mode 100644
index 0000000..b1a66d0
--- /dev/null
+++ b/src/suncertify/db/data/column/StringColumnSearcher.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+import suncertify.db.RecordNotFoundException;
+import suncertify.db.data.Table;
+
+public class StringColumnSearcher implements ColumnSearcher {
+
+ /**
+ * @see suncertify.db.data.column.ColumnSearcher#searchColumn(suncertify.db.data.Table, suncertify.db.data.column.Column, java.lang.String, java.util.Set)
+ */
+ @Override
+ public boolean searchColumn(Table table, Column column,String searchString,String columnData) throws RecordNotFoundException {
+ return columnData.contains(searchString);
+ }
+
+ public void indexColumn(Table table, Column column,String columnData) throws RecordNotFoundException {
+
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/db/data/column/StringObjectConverter.java b/src/suncertify/db/data/column/StringObjectConverter.java
new file mode 100644
index 0000000..db19b95
--- /dev/null
+++ b/src/suncertify/db/data/column/StringObjectConverter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+
+public class StringObjectConverter extends AbstractColumnObjectConverter {
+
+ public StringObjectConverter() {
+ super(String.class);
+ }
+
+ /**
+ * @see suncertify.db.data.column.ColumnObjectConverter#decodeInterface(suncertify.db.data.column.Column, java.lang.String)
+ */
+ @Override
+ public Object decodeInterface(Column c, String object) {
+ return object;
+ }
+
+ /**
+ * @see suncertify.db.data.column.ColumnObjectConverter#encodeInterface(suncertify.db.data.column.Column, java.lang.Object)
+ */
+ @Override
+ public String encodeInterface(Column c, Object object) {
+ if ((object instanceof String)==false) {
+ throw new IllegalStateException("Can only do string");
+ }
+ return (String)object;
+ }
+}
diff --git a/src/suncertify/db/data/column/TextIndex.java b/src/suncertify/db/data/column/TextIndex.java
new file mode 100644
index 0000000..fb9e7cb
--- /dev/null
+++ b/src/suncertify/db/data/column/TextIndex.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.db.data.column;
+
+import java.nio.MappedByteBuffer;
+import java.nio.charset.Charset;
+
+public class TextIndex {
+
+ /** The NMAP'ed bytebuffer to the file database on disk. */
+ private MappedByteBuffer byteBuffer = null;
+ /** Object to locking the bytebuffer. */
+ private Object byteBufferLock = new Object();
+
+
+
+ /// use own table imple for Hit object, word obj == ~~
+}
+
+class HitIndex {
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/src/suncertify/db/data/column/package.html b/src/suncertify/db/data/column/package.html
new file mode 100644
index 0000000..081578f
--- /dev/null
+++ b/src/suncertify/db/data/column/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+Database columns objects.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/db/data/package.html b/src/suncertify/db/data/package.html
new file mode 100644
index 0000000..183e9cb
--- /dev/null
+++ b/src/suncertify/db/data/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+Related data database classes.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/db/package.html b/src/suncertify/db/package.html
new file mode 100644
index 0000000..0c037e1
--- /dev/null
+++ b/src/suncertify/db/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+The database for doing work on the binary data files.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/models/HotelRoom.java b/src/suncertify/models/HotelRoom.java
new file mode 100644
index 0000000..0647ee1
--- /dev/null
+++ b/src/suncertify/models/HotelRoom.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.models;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Date;
+
+
+/**
+ * The HotelRoom model class.
+ *
+ * This stores all the data which belongs to one hotelroom.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class HotelRoom implements Externalizable {
+
+ private Integer id = null;
+ private String name = null;
+ private String location = null;
+ private Integer size = null;
+ private Boolean smoking = null;
+ private Long priceRate = null;
+ private Date dateAvailable = null;
+ private Integer customerId = null;
+
+ /**
+ * @return the id
+ */
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * @param id the id to set
+ */
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ /**
+ * @return the name
+ *
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return the location
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * @param location the location to set
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * @return the size
+ */
+ public Integer getSize() {
+ return size;
+ }
+
+ /**
+ * @param size the size to set
+ */
+ public void setSize(Integer size) {
+ this.size = size;
+ }
+
+ /**
+ * @return the smoking
+ */
+ public Boolean getSmoking() {
+ return smoking;
+ }
+
+ /**
+ * @param smoking the smoking to set
+ */
+ public void setSmoking(Boolean smoking) {
+ this.smoking = smoking;
+ }
+
+ /**
+ * @return the priceRate
+ */
+ public Long getPriceRate() {
+ return priceRate;
+ }
+
+ /**
+ * @param priceRate the priceRate to set
+ */
+ public void setPriceRate(Long priceRate) {
+ this.priceRate = priceRate;
+ }
+
+ /**
+ * @return the dateAvailable
+ */
+ public Date getDateAvailable() {
+ return dateAvailable;
+ }
+
+ /**
+ * @param dateAvailable the dateAvailable to set
+ */
+ public void setDateAvailable(Date dateAvailable) {
+ this.dateAvailable = dateAvailable;
+ }
+
+ /**
+ * @return the customerId
+ */
+ public Integer getCustomerId() {
+ return customerId;
+ }
+
+ /**
+ * @param customerId the customerId to set
+ */
+ public void setCustomerId(Integer customerId) {
+ this.customerId = customerId;
+ }
+
+
+
+ /**
+ * We support reading null fields because BU checks should not take place here.
+ * This is here for speed.
+ * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+ */
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ boolean nullRes = in.readBoolean();
+ id = nullRes?in.readInt():null;
+ nullRes = in.readBoolean();
+ name = nullRes?in.readUTF():null;
+ nullRes = in.readBoolean();
+ location = nullRes?in.readUTF():null;
+ nullRes = in.readBoolean();
+ size = nullRes?in.readInt():null;
+ nullRes = in.readBoolean();
+ smoking = nullRes?in.readBoolean():null;
+ nullRes = in.readBoolean();
+ priceRate = nullRes?in.readLong():null;
+ nullRes = in.readBoolean();
+ dateAvailable = nullRes?new Date(in.readLong()):null;
+ nullRes = in.readBoolean();
+ customerId = nullRes?in.readInt():null;
+ }
+
+ /**
+ * We support writeing null fields because BU checks should not take place here.
+ * This is here for speed.
+ * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+ */
+ public void writeExternal(ObjectOutput out) throws IOException {
+
+ if (id==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeInt (id);
+ }
+ if (name==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeUTF (name);
+ }
+ if (location==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeUTF (location);
+ }
+ if (size==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeInt (size);
+ }
+ if (smoking==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeBoolean (smoking);
+ }
+ if (priceRate==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeLong (priceRate);
+ }
+ if (dateAvailable==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeLong (dateAvailable.getTime());
+ }
+ if (customerId==null) {
+ out.writeBoolean (false);
+ } else {
+ out.writeBoolean (true);
+ out.writeInt (customerId);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/models/HotelRoomDBConverter.java b/src/suncertify/models/HotelRoomDBConverter.java
new file mode 100644
index 0000000..74d62c0
--- /dev/null
+++ b/src/suncertify/models/HotelRoomDBConverter.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.models;
+
+import java.util.Date;
+
+import suncertify.db.data.Table;
+import suncertify.db.data.column.Column;
+
+/**
+ * The HotelRoomDBConverter
+ *
+ * Converts betreen the HotelRoom model and the binany DB data.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class HotelRoomDBConverter {
+
+ private Table table = null;
+
+ /**
+ * Creates an HotelRoomDBConverter for the given table.
+ * @param table The DB table to do converts for.
+ */
+ public HotelRoomDBConverter(Table table) {
+ if (table==null) {
+ throw new NullPointerException("Table may not be null.");
+ }
+ this.table=table;
+ }
+
+ /**
+ * Converts String[] data to en HotelRoom data model.
+ *
+ * @param data The string data.
+ * @return The HotelRoom model.
+ */
+ public HotelRoom decode(String[] data) {
+ HotelRoom hr = new HotelRoom();
+ for (int i=0;i<=6;i++) {
+ Column c = table.getColumns().get(i+1); // todo bounds
+ Object object = c.getObjectConverter().decodeInterface(c,data[i]);
+ switch(i) {
+ case 0:
+ hr.setName((String)object);
+ break;
+ case 1:
+ hr.setLocation((String)object);
+ break;
+ case 2:
+ hr.setSize((Integer)object);
+ break;
+ case 3:
+ hr.setSmoking((Boolean)object);
+ break;
+ case 4:
+ hr.setPriceRate((Long)object);
+ break;
+ case 5:
+ hr.setDateAvailable((Date)object);
+ break;
+ case 6:
+ hr.setCustomerId((Integer)object);
+ break;
+ default:
+ throw new IllegalStateException("out of bounds");
+ }
+ }
+ return hr;
+ }
+
+ /**
+ * Converts an HotelRoom model to the String data.
+ *
+ * @param hr The hotelRoom model
+ * @return The data as an String array.
+ */
+ public String[] encode(HotelRoom hr) {
+ String[] result = new String[7];
+ for (int i=0;i<=6;i++) {
+ Column c = table.getColumns().get(i+1); // todo bounds
+ Object object = null;
+ switch(i) {
+ case 0:
+ object = hr.getName();
+ break;
+ case 1:
+ object = hr.getLocation();
+ break;
+ case 2:
+ object = hr.getSize();
+ break;
+ case 3:
+ object = hr.getSmoking();
+ break;
+ case 4:
+ object = hr.getPriceRate();
+ break;
+ case 5:
+ object = hr.getDateAvailable();
+ break;
+ case 6:
+ object = hr.getCustomerId();
+ break;
+ default:
+ throw new IllegalStateException("out of bounds");
+ }
+ String r = null;
+ if (object!=null) {
+ r = c.getObjectConverter().encodeInterface(c, object);
+ } else {
+ r = "";
+ }
+ result[i]=r;
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/models/package.html b/src/suncertify/models/package.html
new file mode 100644
index 0000000..0c67375
--- /dev/null
+++ b/src/suncertify/models/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+The models used in the application.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/net/NIOChangeRequest.java b/src/suncertify/net/NIOChangeRequest.java
new file mode 100644
index 0000000..59bd289
--- /dev/null
+++ b/src/suncertify/net/NIOChangeRequest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+
+package suncertify.net;
+
+import java.nio.channels.SocketChannel;
+
+/**
+ * The NIOChangeRequest is used by the NIOConnector to register of change the operation key.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class NIOChangeRequest {
+
+ public enum ChangeType {
+ REGISTER,
+ CHANGEOPS
+ }
+
+ public SocketChannel socket;
+ public ChangeType type;
+ public int ops;
+
+ public NIOChangeRequest(SocketChannel socket, ChangeType type, int ops) {
+ this.socket = socket;
+ this.type = type;
+ this.ops = ops;
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/net/NIOConnector.java b/src/suncertify/net/NIOConnector.java
new file mode 100644
index 0000000..2d4b567
--- /dev/null
+++ b/src/suncertify/net/NIOConnector.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import suncertify.net.NIOChangeRequest.ChangeType;
+
+/**
+ * The NIOConnector is an abstract base class for creating NIO client/servers.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+abstract public class NIOConnector implements Runnable {
+
+ /** The hostAddress to bind or connect to. */
+ private InetAddress hostAddress;
+ /** The port of the hostAddress */
+ private int port;
+
+ /** The selector we'll be monitoring */
+ protected Selector selector;
+
+ /** The buffer into which we'll read data when it's available */
+ private ByteBuffer readBuffer = ByteBuffer.allocate(8192);
+
+ /** A list of PendingChange instances */
+ private List pendingChanges = null;
+
+ /** Maps a SocketChannel to a list of ByteBuffer instances */
+ private Map> pendingData = null;
+
+ /** The connection counter */
+ final protected AtomicInteger connectsCounter = new AtomicInteger();
+
+ /** Counter for total connection */
+ volatile protected int totalConnections = 0;
+
+ /** The logger to log to */
+ private Logger logger = Logger.getLogger(NIOConnector.class.getName());
+
+ /** Flag to stop thread */
+ volatile protected boolean stop = false;
+
+ /**
+ * Creates an abstract NIOConnector
+ * @param hostAddress
+ * @param port
+ * @throws IOException
+ */
+ public NIOConnector(InetAddress hostAddress, int port) {
+ this.hostAddress = hostAddress;
+ this.port = port;
+ pendingChanges = new LinkedList();
+ pendingData = new HashMap>();
+ }
+
+ abstract public void start() throws IOException;
+ abstract public void stop() throws IOException;
+
+ /**
+ * Sends data to the socket, appends it to the pendingData queu
+ * @param socket
+ * @param data
+ */
+ protected void send(SocketChannel socket,ByteBuffer data) {
+ // Indicate we want the interest ops set changed
+ addChangeRequest(socket, NIOChangeRequest.ChangeType.CHANGEOPS, SelectionKey.OP_WRITE);
+
+ // And queue the data we want written
+ synchronized (pendingData) {
+ List queue = pendingData.get(socket);
+ if (queue == null) {
+ queue = new ArrayList();
+ pendingData.put(socket, queue);
+ }
+ queue.add(data);
+ }
+
+ // Finally, wake up our selecting thread so it can make the required changes
+ selector.wakeup();
+ }
+
+ /**
+ * The NIO IO selector runnable
+ */
+ public void run() {
+ logger.info("NIO network thread started.");
+ while (stop==false) {
+ try {
+ synchronized (pendingChanges) {
+ int totalChanges = pendingChanges.size();
+ if (totalChanges>0) {
+ for (int i=0;i selectedKeys = selector.selectedKeys().iterator();
+ while (selectedKeys.hasNext()) {
+ SelectionKey key = selectedKeys.next();
+ selectedKeys.remove();
+
+ if (key.isValid()==false) {
+ continue;
+ }
+
+
+ // check on session object
+ Object o = key.attachment();
+ //mm we can only set attech ment after key is accepted
+ if (o==null & key.isAcceptable()==false) {
+ o = new Integer(connectsCounter.getAndIncrement());
+ key.attach(o);
+ totalConnections++;
+ logger.info("Init Connection: "+key.attachment()+" total: "+totalConnections);
+ }
+
+ // Check what event is available and deal with it
+ try {
+ if (key.isConnectable()) {
+ connect(key);
+ } else if (key.isAcceptable()) {
+ accept(key);
+ } else if (key.isReadable()) {
+ read(key);
+ } else if (key.isWritable()) {
+ write(key);
+ } else {
+ throw new IOException("NIO error, key could not be processed.");
+ }
+ } catch (IOException ioe) {
+ Integer conNr = (Integer)key.attachment();
+ if (ioe.getMessage()!=null && ioe.getMessage().contains("reset by peer")) {
+ logger.log(Level.WARNING,"Connection: "+conNr+" has disconnected on error: "+ioe.getMessage());
+ } else {
+ logger.log(Level.WARNING,"Connection: "+conNr+" has disconnected on error: "+ioe.getMessage(),ioe);
+ }
+ key.cancel();
+ }
+
+ if (key.isValid()==false) {
+ totalConnections--;
+ }
+ }
+ } catch (Exception e) {
+ logger.log(Level.WARNING,"Error in NIO Thread: "+e.getMessage(),e);
+ }
+ }
+
+ // cleaning resources
+ pendingChanges.clear();
+ pendingData.clear();
+ totalConnections = 0;
+ logger.info("NIO network thread stoped.");
+ }
+
+ /**
+ * Is called when the key is connected, emty method.
+ * @param key
+ * @throws IOException
+ */
+ protected void connect(SelectionKey key) throws IOException {
+ }
+
+ /**
+ * Is called when the key is accepted, emty method.
+ * @param key
+ * @throws IOException
+ */
+ protected void accept(SelectionKey key) throws IOException {
+ }
+
+ /**
+ * Reads the data from the key and hands it over to processData
+ * @param key
+ * @throws IOException
+ */
+ protected void read(SelectionKey key) throws IOException {
+ SocketChannel socketChannel = (SocketChannel) key.channel();
+
+ // Clear out our read buffer so it's ready for new data
+ readBuffer.clear();
+
+ int numRead = socketChannel.read(readBuffer);
+ if (numRead == -1) {
+ key.cancel();
+ return;
+ }
+ readBuffer.rewind();
+ readBuffer.limit(numRead);
+ processData(socketChannel, readBuffer);
+ }
+
+ /**
+ * Processes the readed data.
+ *
+ * @param socket
+ * @param readBuffer
+ * @throws IOException
+ */
+ abstract void processData(SocketChannel socket, ByteBuffer readBuffer) throws IOException;
+
+ /**
+ * Writes the data from the key to the pendingData queue
+ * @param key
+ * @throws IOException
+ */
+ protected void write(SelectionKey key) throws IOException {
+ SocketChannel socketChannel = (SocketChannel) key.channel();
+
+ synchronized (pendingData) {
+ List queue = pendingData.get(socketChannel);
+ // Write until there's not more data ...
+ while (queue.isEmpty()==false) {
+ ByteBuffer buf = queue.get(0);
+ socketChannel.write(buf);
+ if (buf.remaining() > 0) {
+ break; // ... or the socket's buffer fills up
+ }
+ queue.remove(0);
+ }
+ if (queue.isEmpty()) {
+ // We wrote away all data, so we're no longer interested
+ // in writing on this socket. Switch back to waiting for data.
+ addChangeRequest(socketChannel, NIOChangeRequest.ChangeType.CHANGEOPS, SelectionKey.OP_READ);
+ }
+ }
+ }
+
+ /**
+ * Opens the default Selector.
+ * @return
+ * @throws IOException
+ */
+ protected Selector initSelector() throws IOException {
+ return SelectorProvider.provider().openSelector();
+ }
+
+ /**
+ * Adds an ChangeRequest for the socket
+ * @param socket
+ * @param type
+ * @param ops
+ */
+ public void addChangeRequest(SocketChannel socket, ChangeType type, int ops) {
+ synchronized (pendingChanges) {
+ pendingChanges.add(new NIOChangeRequest(socket, type, ops));
+ }
+ }
+
+ /**
+ * @return the hostAddress
+ */
+ public InetAddress getHostAddress() {
+ return hostAddress;
+ }
+
+ /**
+ * @return the port
+ */
+ public int getPort() {
+ return port;
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/net/NetworkClient.java b/src/suncertify/net/NetworkClient.java
new file mode 100644
index 0000000..2ce1746
--- /dev/null
+++ b/src/suncertify/net/NetworkClient.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.net;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * The NetworkClient
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class NetworkClient extends NetworkNIOConnector {
+
+ /** Maps a SocketChannel to a RspHandler */
+ private Map responseHandlers = null;
+ /** Keeps track of the request IDs. */
+ private AtomicInteger requestIds = new AtomicInteger(0);
+ /** The sockets wo connect to */
+ private SocketChannel socket = null;
+ /** The logger to log to. */
+ private Logger logger = Logger.getLogger(NetworkClient.class.getName());
+ /** The left over buffer, used to fix multi threaded requests. */
+ private byte[] leftBuf = null;
+
+ /**
+ * Creates an NetworkClient.
+ *
+ * @param hostAddress
+ * @param port
+ * @throws IOException
+ */
+ public NetworkClient(InetAddress hostAddress, int port) throws IOException {
+ super(hostAddress,port);
+ responseHandlers = Collections.synchronizedMap(new HashMap(100));
+ }
+
+ /**
+ * We override stop to clean our private resources here
+ * @see suncertify.net.NetworkNIOConnector#stop()
+ */
+ @Override
+ public void stop() throws IOException {
+ try {
+ super.stop();
+ } finally {
+ leftBuf = null;
+ try {
+ if (socket!=null) { // start,stop when nothing happend, then socket is still null.
+ socket.close();
+ }
+ } finally {
+ responseHandlers.clear();
+ }
+ }
+ }
+
+ /**
+ * Sends an NetworkRequest to the server.
+ * @param request
+ * @param handler
+ * @throws IOException
+ */
+ public void send(NetworkRequest request, NetworkResponseHandler handler) throws IOException {
+ // We connect on first send
+ if (socket==null) {
+ socket = initiateConnection();
+ }
+ // Register the response handler
+ responseHandlers.put(handler.getNetworkRequest().getRequestId(), handler);
+
+ // send object to socket
+ sendObject(socket,request);
+ }
+
+ /**
+ * Process data from the server.
+ */
+ @Override
+ protected void processData(SocketChannel socket,ByteBuffer readBuffer) throws IOException {
+
+ // we should make sure the happens less often or not at all, because the allocate is an heavy function.
+ if (leftBuf!=null) {
+ if (logger.isLoggable(Level.FINE)) {
+ logger.fine("Fixing data: appendSize: "+leftBuf.length+" buf: "+readBuffer.remaining());
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(8192+leftBuf.length+readBuffer.remaining());
+ buffer.limit(leftBuf.length+readBuffer.remaining());
+
+ buffer.put(leftBuf);
+ buffer.put(readBuffer);
+
+ buffer.rewind();
+ readBuffer.clear();
+ readBuffer = buffer;
+ leftBuf = null;
+ }
+
+ ByteBuffer leftOver = receiveObject(socket,readBuffer, new ObjectHandler() {
+ public void processObject(Object object) {
+ NetworkResponse response = (NetworkResponse)object;
+ NetworkResponseHandler handler = responseHandlers.remove(response.getRequestId());
+ if (handler==null) {
+ logger.warning("Error no handler for read data of request "+response.getRequestId());
+ return;
+ }
+ // And pass the response to it
+ handler.handleResponse(response);
+ }
+ },0);
+ if (leftOver==null) {
+ return; // all oke
+ }
+
+ // save left over for when next bytes come in.
+ byte[] data2 = new byte[leftOver.remaining()];
+ leftOver.get(data2);
+ leftBuf = data2;
+
+ if (logger.isLoggable(Level.FINE)) {
+ ByteArrayInputStream in = new ByteArrayInputStream(leftBuf);
+ ObjectInputStream objstream = new ObjectInputStream(in);
+ int objectSize = objstream.readInt();
+ objstream.close();
+
+ logger.fine("Pasting LeftOver size: "+data2.length+" Next object size: "+objectSize);
+ }
+ }
+
+ /**
+ * Creates our connection to the server.
+ * @return
+ * @throws IOException
+ */
+ private SocketChannel initiateConnection() throws IOException {
+ // Create nonblocking socket channel
+ SocketChannel socketChannel = SocketChannel.open();
+ socketChannel.configureBlocking(false);
+
+ // Connect to server
+ socketChannel.connect(new InetSocketAddress(getHostAddress(),getPort()));
+ socketChannel.finishConnect();
+
+ // select register on selector in nio thread
+ addChangeRequest(socketChannel, NIOChangeRequest.ChangeType.REGISTER, SelectionKey.OP_CONNECT);
+ return socketChannel;
+ }
+
+ /**
+ * Returns an InvocationHandler for creating proxed objects.
+ * @param obj
+ * @return
+ */
+ public InvocationHandler getInvocationHandler(Class> obj) {
+ return new NetworkInvocationHandler(obj,this);
+ }
+
+ /**
+ * InvocationHandler for handeling the method calls of the proxy bean.
+ *
+ */
+ private class NetworkInvocationHandler implements InvocationHandler {
+ private String beanName = null;
+ private NetworkClient client = null;
+
+ public NetworkInvocationHandler(Class> obj,NetworkClient client) {
+ beanName = obj.getName();
+ this.client=client;
+ }
+
+ public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
+ // create request and fill it.
+ NetworkRequest request = new NetworkRequest(requestIds.getAndIncrement());
+ request.setRequestBean(beanName, method.getName(), args);
+ // Create handler for when we get response.
+ NetworkResponseHandler handler = new NetworkResponseHandler(request,client);
+ // send and wait..
+ client.send(request, handler);
+ handler.waitForResponse();
+ // we have it.
+ Object result = handler.getResult();
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/net/NetworkNIOConnector.java b/src/suncertify/net/NetworkNIOConnector.java
new file mode 100644
index 0000000..281ffd2
--- /dev/null
+++ b/src/suncertify/net/NetworkNIOConnector.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.net;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.logging.Logger;
+
+/**
+ * The NetworkNIOConnector for creating object based serialezed nio client/servers
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+abstract public class NetworkNIOConnector extends NIOConnector {
+
+ /** The logger to log to. */
+ private Logger logger = Logger.getLogger(NetworkNIOConnector.class.getName());
+
+ /** Counter for send Objects */
+ volatile private int sendObjectCounter = 0;
+
+ /** indicating the milisecond value for 1min.. */
+ static final private long ONCE_PER_MINUTE = 1000*60;
+
+ /** Timer object to print stats. */
+ private Timer statsTimer = null;
+
+ /**
+ * Creates an NetworkNIOConnector.
+ *
+ * @param hostAddress
+ * @param port
+ * @throws IOException
+ */
+ public NetworkNIOConnector(InetAddress hostAddress, int port) throws IOException {
+ super(hostAddress,port);
+ }
+
+ /**
+ * Starts this Connector by creating the selector
+ * and starts the backend nio thread.
+ * Creates an Timer for printing the stats of this connector.
+ */
+ @Override
+ public void start() throws IOException {
+ // create selectort
+ selector = initSelector();
+
+ // Start the backend thread
+ Thread t = new Thread(this,"net-nio");
+ t.setDaemon(true);
+ t.start();
+
+ // start the stats timers
+ statsTimer = new Timer("net-stats");
+ statsTimer.scheduleAtFixedRate(new TimerTask() {
+ public void run() {
+ try {
+ logger.info("sendObject() Statistics"+
+ " TPM: "+sendObjectCounter+
+ " TPS: "+(sendObjectCounter/60)+
+ " totalEstablisedConnections: "+totalConnections+
+ " totalHandledConnections: "+connectsCounter.get());
+ } finally {
+ sendObjectCounter=0;
+ }
+ }
+ }, 0, ONCE_PER_MINUTE);
+
+ // reset stats counters
+ sendObjectCounter=0;
+ }
+
+ /**
+ * Stops the connector and backend threads.
+ * cleaning all resources and closing the connections/selector.
+ */
+ @Override
+ public void stop() throws IOException {
+ if (stop) {
+ throw new IllegalStateException("Can't stop already stopped connector.");
+ }
+ stop = true; // note: thread stops his resources (and selector)
+ try {
+ selector.wakeup();
+ } finally {
+ try {
+ statsTimer.cancel();
+ } finally {
+
+ // simple stop check
+ int max=10;
+ while (totalConnections!=0 & max!=0) {
+ try {
+ max--;
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ }
+ }
+ selector.close();
+ }
+ }
+ }
+
+ public void sendObject(SocketChannel socket,Object object) throws IOException {
+
+ // write request
+ ByteArrayOutputStream dataArray = new ByteArrayOutputStream(256); // it grows when needed
+ ObjectOutputStream objOutstream = new ObjectOutputStream(dataArray);
+ objOutstream.writeObject(object);
+ objOutstream.close();
+
+ int objectSize = dataArray.size();
+
+ // write request size
+ ByteArrayOutputStream dataArray2 = new ByteArrayOutputStream(objectSize+8);
+ ObjectOutputStream objOutstream2 = new ObjectOutputStream(dataArray2); // todo: remove this new statement
+ objOutstream2.writeInt(objectSize);
+ objOutstream2.close();
+
+ // add request object
+ dataArray2.write(dataArray.toByteArray());
+ super.send(socket, ByteBuffer.wrap(dataArray2.toByteArray()));
+ sendObjectCounter++;
+ }
+
+ public ByteBuffer receiveObject(SocketChannel socket,ByteBuffer readBuffer,ObjectHandler objectHandler,int offset) throws IOException {
+
+ if (readBuffer.limit()<8) {
+ logger.fine("Missing bytes, could not read object Size bufLimit: "+readBuffer.limit());
+ return readBuffer;
+ }
+
+ ByteArrayInputStream in = new ByteArrayInputStream(readBuffer.array(),0,readBuffer.limit());
+
+ //System.out.println("in: "+in.available()+" off: "+offset);
+ if ((in.available()-offset)<8) {
+ logger.info("--------------------------------- TODO: error, fix");
+ return readBuffer;
+ }
+
+ in.skip(offset);
+
+ ObjectInputStream objstream = new ObjectInputStream(in);
+ int objectSize = objstream.readInt();
+ objstream.close();
+
+ if (objectSize>in.available()) {
+ logger.fine("Not all bytes gotten: objSize: "+objectSize+" inA: "+in.available());
+ readBuffer.position(offset);
+ return readBuffer;
+ }
+
+
+ objstream = new ObjectInputStream(in);
+ Object result = null;
+ try {
+ result = objstream.readObject();
+ objectHandler.processObject(result);
+ } catch (ClassNotFoundException e) {
+ throw new IOException("Could not load object class: "+e.getMessage(),e);
+ } finally {
+ if (in.available()>0 ) {
+ logger.fine("==== dubbel request added left over: "+in.available()+" offset: "+(readBuffer.limit()-in.available()));
+ return receiveObject(socket,readBuffer,objectHandler,(readBuffer.limit()-in.available()));
+ //return null;
+ }
+ }
+ return null;
+ }
+
+ public interface ObjectHandler {
+ public void processObject(Object object);
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/net/NetworkRequest.java b/src/suncertify/net/NetworkRequest.java
new file mode 100644
index 0000000..f84b909
--- /dev/null
+++ b/src/suncertify/net/NetworkRequest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.net;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * The NetworkRequest is the object sent to the server for invoking a method on a bean.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class NetworkRequest implements Externalizable {
+
+ /** The request ID, should be an uniq number */
+ private Integer requestId = null;
+
+ /** The beanName we do the request on. */
+ private String beanName = null;
+
+ /** The methodName of the bean to call. */
+ private String methodName = null;
+
+ /** The argruments of the method name. */
+ private Object[] methodArgs = null;
+
+ public NetworkRequest() {
+ }
+
+ /**
+ * Creates an NetworkRequest with an given requestId.
+ * @param requestId The requestId of the response.
+ */
+ public NetworkRequest(Integer requestId) {
+ if (requestId==null) {
+ throw new NullPointerException("can't handle null requestID.");
+ }
+ this.requestId=requestId;
+ }
+
+ /**
+ * Sets and check all info for request.
+ * @param beanName
+ * @param methodName
+ * @param methodArgs
+ */
+ public void setRequestBean(String beanName,String methodName,Object[] methodArgs) {
+ if (beanName==null) {
+ throw new NullPointerException("Can't request null beanName.");
+ }
+ if (beanName.isEmpty()) {
+ throw new IllegalArgumentException("Can't request empty beanName.");
+ }
+ if (methodName==null) {
+ throw new NullPointerException("Can't request null methodName");
+ }
+ if (methodName.isEmpty()) {
+ throw new NullPointerException("Can't request empty methodName");
+ }
+ this.beanName=beanName;
+ this.methodName=methodName;
+ this.methodArgs=methodArgs;
+ }
+
+ /**
+ * Returns the request id.
+ * @return The requestId
+ */
+ public Integer getRequestId() {
+ return requestId;
+ }
+
+ /**
+ * Returns the beanName
+ * @return The beanName to request
+ */
+ public String getBeanName() {
+ return beanName;
+ }
+
+ /**
+ * Returns the methodName
+ * @return The methodName to request
+ */
+ public String getMethodName() {
+ return methodName;
+ }
+
+ /**
+ * Returns the methodArgs
+ * @return The methodArgs to invoke on bean.
+ */
+ public Object[] getMethodArgs() {
+ return methodArgs;
+ }
+
+ /**
+ * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+ */
+ @Override
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ requestId = in.readInt();
+ beanName = in.readUTF();
+ methodName = in.readUTF();
+ int argsCount = in.readInt();
+ if (argsCount==0) {
+ return;
+ }
+ methodArgs = new Object[argsCount];
+ for (int i=0;i
+
+
+
+
+
+The network layer.
+Bases on a NIO sockets with object stream serilatition.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/server/ServerException.java b/src/suncertify/server/ServerException.java
new file mode 100644
index 0000000..25c19b4
--- /dev/null
+++ b/src/suncertify/server/ServerException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.server;
+
+/**
+ * The ServerException is thrown when the server has an exception.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+@SuppressWarnings("serial")
+public class ServerException extends Exception {
+
+ /**
+ * Message constructor
+ * @param message The error message
+ */
+ public ServerException(String message) {
+ super(message);
+ }
+
+ /**
+ * Full constructor
+ * @param message The error message
+ * @param exception The error exception
+ */
+ public ServerException(String message,Exception exception) {
+ super(message,exception);
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/server/ServerManager.java b/src/suncertify/server/ServerManager.java
new file mode 100644
index 0000000..2de7d44
--- /dev/null
+++ b/src/suncertify/server/ServerManager.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.server;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * The ServerManager manages server beans and create and inject depencicies on the beans.
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class ServerManager {
+
+ private ThreadPoolExecutor threadPoolExecutor = null;
+ private Map> beans = new HashMap>(5);
+ private Map initBeans = new HashMap(5);
+
+ /**
+ * Creates an ServerManager with an worker pool of threads.
+ */
+ public ServerManager() {
+ // core, max, keepalive
+ threadPoolExecutor = new ThreadPoolExecutor (
+ 3,10,2,
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue(),
+ new NameingThreadFactory("server-")
+ );
+ }
+
+ /**
+ * Starts the serverManager backend thread pool.
+ */
+ public void start() {
+ threadPoolExecutor.prestartAllCoreThreads();
+ }
+
+ /**
+ * Stops the ServerManager backend thread pool.
+ */
+ public void stop() {
+ threadPoolExecutor.shutdown();
+ }
+
+ /**
+ * Execute an Runnable object in the ServerManager thread pool.
+ * @param run The Object to run.
+ */
+ public void execute(Runnable run) {
+ threadPoolExecutor.execute(run);
+ }
+
+ /**
+ * Adds an bean by its class.
+ * @param serverBeanRemote
+ * @param serverBean
+ */
+ public void putServerBean(Class> serverBeanRemote,Class> serverBean) {
+ beans.put(serverBeanRemote.getName(),serverBean);
+ }
+
+ /**
+ * Add an bean which is already created.
+ * @param name
+ * @param serverBean
+ */
+ public void putServerInitBean(String name,Object serverBean) {
+ initBeans.put(name,serverBean);
+ }
+
+ /**
+ * Request an server bean.
+ * This methode also makes sure all depencies are injectes into the bean.
+ *
+ * @param name
+ * @return
+ * @throws ServerException
+ */
+ public Object getServerBean(String name) throws ServerException {
+
+ // let init bean be first
+ if (initBeans.containsKey(name)) {
+ return initBeans.get(name);
+ }
+
+ Class> c = beans.get(name);
+ if (c==null) {
+ throw new ServerException("Could not find bean for: "+name);
+ }
+
+ Object result = null;
+ try {
+ result = c.newInstance();
+ } catch (Exception e) {
+ throw new ServerException("Could error while init bean: '"+name+"' "+e.getMessage(),e);
+ }
+
+ for (Field field:result.getClass().getFields()) {
+ ServerResource resource = field.getAnnotation(ServerResource.class);
+ if (resource==null) {
+ continue;
+ }
+ String beanName = resource.beanName();
+ if ("null".equals(beanName)) {
+ beanName = field.getType().getName();
+ }
+ Object bean = getServerBean(beanName);
+ if (bean==null) {
+ continue;
+ }
+ try {
+ field.set(result,bean);
+ } catch (Exception e) {
+ throw new ServerException("Could not load resource for bean: "+name+" resource: "+beanName);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Get all the Beans know in the server.
+ * @return
+ */
+ public List getServerBeanNames() {
+ List result = new ArrayList(beans.keySet());
+ result.addAll(initBeans.keySet());
+ return result;
+ }
+
+ /**
+ * Get local InvocationHandler which is used for none network mode for this server.
+ * @return
+ */
+ public InvocationHandler getLocalInvocationHandler() {
+ return new LocalRequestProxy(this);
+ }
+
+ /**
+ * Custum ThreadFactory for getting thread nameing correctly.
+ */
+ private class NameingThreadFactory implements ThreadFactory {
+ final ThreadGroup group;
+ final AtomicInteger threadNumber = new AtomicInteger(1);
+ final String namePrefix;
+
+ public NameingThreadFactory(String namePrefix) {
+ if (namePrefix==null) {
+ namePrefix = "thread-";
+ }
+ this.namePrefix=namePrefix;
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ group = s.getThreadGroup();
+ } else {
+ group = Thread.currentThread().getThreadGroup();
+ }
+ }
+
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group, r,namePrefix+threadNumber.getAndIncrement(),0);
+ if (t.isDaemon()) {
+ t.setDaemon(false);
+ }
+ if (t.getPriority() != Thread.NORM_PRIORITY) {
+ t.setPriority(Thread.NORM_PRIORITY);
+ }
+ return t;
+ }
+ }
+
+ /**
+ * InvocationHandler for handeling the method calls of the proxy bean.
+ *
+ */
+ private class LocalRequestProxy implements InvocationHandler {
+
+ private ServerManager serverManager = null;
+
+ public LocalRequestProxy(ServerManager serverManager) {
+ this.serverManager=serverManager;
+ }
+
+ public Object invoke(Object proxy, Method method,Object[] args) throws Throwable {
+ Object bean = serverManager.getServerBean(proxy.getClass().getName());
+ int pS = 0;
+ if (args!=null) {
+ pS = args.length;
+ }
+ Class>[] para = new Class[pS];
+ for (int i=0;i getLastBookings(int logID) {
+ List result = new ArrayList(1);
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/server/beans/BookingLogManagerRemote.java b/src/suncertify/server/beans/BookingLogManagerRemote.java
new file mode 100644
index 0000000..2ea2945
--- /dev/null
+++ b/src/suncertify/server/beans/BookingLogManagerRemote.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.server.beans;
+
+import java.util.List;
+
+/**
+ * The BookingLogManagerRemote
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public interface BookingLogManagerRemote {
+
+ public List getLastBookings(int logID);
+}
\ No newline at end of file
diff --git a/src/suncertify/server/beans/HotelRoomManager.java b/src/suncertify/server/beans/HotelRoomManager.java
new file mode 100644
index 0000000..de442ad
--- /dev/null
+++ b/src/suncertify/server/beans/HotelRoomManager.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.server.beans;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import suncertify.db.DB;
+import suncertify.db.DuplicateKeyException;
+import suncertify.db.RecordNotFoundException;
+import suncertify.models.HotelRoom;
+import suncertify.models.HotelRoomDBConverter;
+import suncertify.server.ServerResource;
+
+/**
+ * The HotelRoomManager
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public class HotelRoomManager implements HotelRoomManagerRemote {
+
+ @ServerResource(beanName="hotelRoom.tableBackend")
+ public DB backend = null;
+
+ @ServerResource(beanName="hotelRoom.beanConverter")
+ public HotelRoomDBConverter hotelRoomDBConverter = null;
+
+ /**
+ * @see suncertify.server.beans.HotelRoomManagerRemote#findByCriteria(suncertify.models.HotelRoom)
+ */
+ @Override
+ public List findByCriteria(HotelRoom hotelRoom) throws RecordNotFoundException {
+ String[] data = hotelRoomDBConverter.encode(hotelRoom);
+ int[] result = backend.find(data);
+ List rooms = new ArrayList(result.length);
+ for (int i:result) {
+ String[] rec = backend.read(i);
+ HotelRoom hr = hotelRoomDBConverter.decode(rec);
+ hr.setId(i);
+ rooms.add(hr);
+ }
+ return rooms;
+ }
+
+ /**
+ * @see suncertify.server.beans.HotelRoomManagerRemote#getAllHotelRooms()
+ */
+ @Override
+ public List getAllHotelRooms() throws RecordNotFoundException {
+ HotelRoom room = new HotelRoom();
+ room.setName("a");
+ return findByCriteria(room); // leaven all values null does an select all
+ }
+
+ /**
+ * @see suncertify.server.beans.HotelRoomManagerRemote#delete(suncertify.models.HotelRoom, java.lang.Long)
+ */
+ @Override
+ public void delete(HotelRoom hotelRoom, Long lockId) throws RecordNotFoundException {
+ backend.delete(hotelRoom.getId(), lockId);
+ }
+
+ /**
+ * @see suncertify.server.beans.HotelRoomManagerRemote#merge(suncertify.models.HotelRoom, java.lang.Long)
+ */
+ @Override
+ public void merge(HotelRoom hotelRoom, Long lockId) throws RecordNotFoundException, SecurityException {
+ String[] data = hotelRoomDBConverter.encode(hotelRoom);
+ backend.update(hotelRoom.getId(), data, lockId);
+ }
+
+ /**
+ * @see suncertify.server.beans.HotelRoomManagerRemote#persist(suncertify.models.HotelRoom)
+ */
+ @Override
+ public Integer persist(HotelRoom hotelRoom) throws DuplicateKeyException {
+ String[] data = hotelRoomDBConverter.encode(hotelRoom);
+ return backend.create(data);
+ }
+
+
+ /**
+ * @see suncertify.server.beans.HotelRoomManagerRemote#lockHotelRoom(java.lang.Integer)
+ */
+ @Override
+ public Long lockHotelRoom(Integer id) throws RecordNotFoundException {
+ return backend.lock(id);
+ }
+
+ /**
+ * @see suncertify.server.beans.HotelRoomManagerRemote#unlockHotelRoom(java.lang.Integer, java.lang.Long)
+ */
+ @Override
+ public void unlockHotelRoom(Integer id, Long lockId) throws RecordNotFoundException {
+ backend.unlock(id, lockId);
+ }
+}
\ No newline at end of file
diff --git a/src/suncertify/server/beans/HotelRoomManagerRemote.java b/src/suncertify/server/beans/HotelRoomManagerRemote.java
new file mode 100644
index 0000000..e5b9623
--- /dev/null
+++ b/src/suncertify/server/beans/HotelRoomManagerRemote.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2008 Willem Cazander.
+ * Created for Sun Certified Developer for the Java 2 Platform
+ * Application Submission (Version 1.1.3)
+ */
+
+package suncertify.server.beans;
+
+import java.util.List;
+
+import suncertify.db.DuplicateKeyException;
+import suncertify.db.RecordNotFoundException;
+import suncertify.models.HotelRoom;
+
+/**
+ * The HotelRoomManagerRemote manages the HotelRooms.
+ *
+ *
+ *
+ * @author Willem Cazander
+ * @version 1.0 Dec 14, 2008
+ */
+public interface HotelRoomManagerRemote {
+
+ // TEMP !!!
+ public List getAllHotelRooms() throws RecordNotFoundException;
+
+ /**
+ *
+ * @param hotelRoom
+ * @return
+ * @throws RecordNotFoundException
+ */
+ public List findByCriteria(HotelRoom hotelRoom) throws RecordNotFoundException;
+
+ /**
+ *
+ * @param id
+ * @return
+ * @throws RecordNotFoundException
+ */
+ public Long lockHotelRoom(Integer id) throws RecordNotFoundException;
+
+ /**
+ *
+ * @param id
+ * @param lockId
+ * @throws RecordNotFoundException
+ */
+ public void unlockHotelRoom(Integer id,Long lockId) throws RecordNotFoundException;
+
+ /**
+ * Persists an HotelRoom
+ * @param hotelRoom
+ * @return
+ * @throws DuplicateKeyException
+ */
+ public Integer persist(HotelRoom hotelRoom) throws DuplicateKeyException;
+
+ /**
+ * Merges an HotelRoom
+ * @param hotelRoom
+ * @param lockId
+ * @throws RecordNotFoundException
+ * @throws SecurityException
+ */
+ public void merge(HotelRoom hotelRoom,Long lockId) throws RecordNotFoundException, SecurityException;
+
+ /**
+ * Deletes an HotelRoom
+ * @param hotelRoom
+ * @param lockId
+ * @throws RecordNotFoundException
+ * @throws SecurityException
+ */
+ public void delete(HotelRoom hotelRoom,Long lockId) throws RecordNotFoundException, SecurityException;
+}
\ No newline at end of file
diff --git a/src/suncertify/server/beans/package.html b/src/suncertify/server/beans/package.html
new file mode 100644
index 0000000..6d45005
--- /dev/null
+++ b/src/suncertify/server/beans/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+The server service beans.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/suncertify/server/package.html b/src/suncertify/server/package.html
new file mode 100644
index 0000000..88352f1
--- /dev/null
+++ b/src/suncertify/server/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+The server for executing services.
+
+
+
+
+
Related Documentation
+
+None.
+
+
+
+
+
\ No newline at end of file
diff --git a/test/suncertify/db/DataTest.java b/test/suncertify/db/DataTest.java
new file mode 100644
index 0000000..2c56cc7
--- /dev/null
+++ b/test/suncertify/db/DataTest.java
@@ -0,0 +1,102 @@
+
+
+package suncertify.db;
+
+import suncertify.core.LoadHotelRoomDB;
+import suncertify.db.Data;
+import suncertify.db.data.RowLock;
+import suncertify.db.data.Table;
+import suncertify.models.HotelRoom;
+import suncertify.models.HotelRoomDBConverter;
+import junit.framework.TestCase;
+
+
+public class DataTest extends TestCase {
+
+
+ public void printRecord(String[] record) {
+ for (String value:record) {
+ System.out.print("'");
+ System.out.print(value);
+ System.out.print("'\t");
+ }
+ System.out.println("");
+ }
+
+
+ public void testData() throws Exception {
+
+ DataBaseManager dataBaseManager = new DataBaseManager();
+ new LoadHotelRoomDB().loadTable(dataBaseManager);
+ Table table = dataBaseManager.getHotelRoomTable();
+
+ HotelRoomDBConverter c = new HotelRoomDBConverter(table);
+
+ Data d = new Data(dataBaseManager.getHotelRoomTable());
+ printRecord(d.read(0));
+ printRecord(d.read(1));
+ printRecord(d.read(9));
+
+ String[] first = d.read(8);
+ printRecord(first);
+ HotelRoom hr = c.decode(first);
+ String[] back = c.encode(hr);
+ printRecord(back);
+ //assertEquals(first,back);
+
+ table.closeTable();
+ }
+
+ public void testLock() throws Exception {
+
+ DataBaseManager dataBaseManager = new DataBaseManager();
+ new LoadHotelRoomDB().loadTable(dataBaseManager);
+ Table table = dataBaseManager.getHotelRoomTable();
+
+ String[] interfaceData = {"name-FIELD-Data"+System.nanoTime(),"location-FIELD-DATa","45","true","$349.34","2008/02/13","888"};
+ int row = table.create(interfaceData);
+ System.out.println("NEW Rcords row:"+row);
+
+ RowLock lock = table.lock(2);
+ table.unlock(2, lock.getLockId());
+ lock = table.lock(2);
+ table.unlock(2, lock.getLockId());
+ lock = table.lock(2);
+ table.unlock(2, lock.getLockId());
+
+ // get lock
+ lock = table.lock(2);
+
+ boolean error = false;
+ try {
+ table.delete(20, lock.getLockId());
+ } catch (SecurityException se) {
+ error = true;
+ }
+ assertEquals(true,error);
+
+
+ error = false;
+ try {
+ table.delete(200000, lock.getLockId());
+ } catch (RecordNotFoundException se) {
+ error = true;
+ }
+ assertEquals(true,error);
+
+ //table.delete(2, lock.getLockId());
+ table.unlock(2, lock.getLockId());
+
+ error = false;
+ try {
+ Data d = new Data(table);
+ printRecord(d.read(2));
+ } catch (RecordNotFoundException re) {
+ error = true;
+ }
+ //assertEquals(true,error);
+
+ table.closeTable();
+ }
+
+}
\ No newline at end of file
diff --git a/test/suncertify/db/InsertTest.java b/test/suncertify/db/InsertTest.java
new file mode 100644
index 0000000..0948048
--- /dev/null
+++ b/test/suncertify/db/InsertTest.java
@@ -0,0 +1,111 @@
+
+
+package suncertify.db;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.logging.Logger;
+
+import suncertify.core.LoadHotelRoomDB;
+import suncertify.db.Data;
+import suncertify.db.data.RowLock;
+import suncertify.db.data.Table;
+import suncertify.models.HotelRoom;
+import suncertify.models.HotelRoomDBConverter;
+import junit.framework.TestCase;
+
+
+public class InsertTest extends TestCase {
+
+
+ public void printRecord(String[] record) {
+ StringBuilder buf = new StringBuilder(200);
+ for (String value:record) {
+ buf.append("'");
+ buf.append(value);
+ buf.append("'\t");
+ }
+ buf.append("");
+
+ Logger.getAnonymousLogger().info("data: "+buf);
+ }
+
+ public List getKamers() throws Exception {
+
+ URL u = new URL("url-not-there......");
+ InputStream in = u.openStream();
+
+ InputStreamReader fileReader = new InputStreamReader(in,"UTF-8");
+ LineNumberReader lineReader = new LineNumberReader(fileReader);
+
+ List result = new ArrayList(200);
+
+ String line = null;
+ int i = 0;
+ while ((line = lineReader.readLine()) !=null) {
+ if (line.contains("offerid")) {
+ continue; // skip header
+ }
+ i++;
+
+ String[] d = line.split("\t");
+
+ HotelRoom r = new HotelRoom();
+ r.setDateAvailable(new Date()); // d[11]
+
+ String s = d[1];
+ if (s.length()>63) {
+ s = s.substring(0,63);
+ }
+ r.setLocation(s);
+
+ if ("".equals(s)) {
+ continue;
+ }
+
+ s = d[2];
+ if (s.length()>63) {
+ s = s.substring(0,63);
+ }
+ r.setName(s);
+
+ if ("".equals(s)) {
+ continue;
+ }
+
+ r.setCustomerId(i);
+ r.setPriceRate(new Long(d[5]));
+ r.setSize(1);
+ r.setSmoking(false);
+
+ result.add(r);
+ }
+
+
+
+ return result;
+ }
+
+
+ public void testData() throws Exception {
+ DataBaseManager dataBaseManager = new DataBaseManager();
+ new LoadHotelRoomDB().loadTable(dataBaseManager);
+ Table table = dataBaseManager.getHotelRoomTable();
+
+ Data d = new Data(dataBaseManager.getHotelRoomTable());
+ HotelRoomDBConverter c = new HotelRoomDBConverter(table);
+
+ for (HotelRoom r:getKamers()) {
+ String[] data = c.encode(r);
+ printRecord(data);
+ d.create(data);
+ }
+
+ table.closeTable();
+ }
+}
\ No newline at end of file
diff --git a/test/suncertify/net/StartServer.java b/test/suncertify/net/StartServer.java
new file mode 100644
index 0000000..c9bace3
--- /dev/null
+++ b/test/suncertify/net/StartServer.java
@@ -0,0 +1,59 @@
+
+package suncertify.net;
+
+import java.io.IOException;
+import java.nio.channels.SocketChannel;
+
+import suncertify.core.LoadHotelRoomDB;
+import suncertify.core.LoadServerBeans;
+import suncertify.core.NetworkServerWorker;
+import suncertify.db.DataBaseManager;
+import suncertify.net.NetworkServer.ServerBackendProvider;
+import suncertify.server.ServerManager;
+
+/**
+ * Starts the server.
+ *
+ * Currently max: (note: fully cached results)
+ * Dec 15, 2008 1:19:01 AM suncertify.net.NetworkNIOConnector$1 run
+ * INFO: sendObject() Statistics TPM: 86326 TPS: 1438
+ *
+ * @author Willem Cazander
+ */
+public class StartServer {
+
+ private NetworkServer server = null;
+
+ public void start() throws Exception {
+ DataBaseManager dataBaseManager = new DataBaseManager();
+ dataBaseManager.start();
+ final ServerManager serverManager = new ServerManager();
+ serverManager.start();
+
+ new LoadHotelRoomDB().loadTable(dataBaseManager);
+ new LoadServerBeans().loadBeans(serverManager,dataBaseManager);
+
+ ServerBackendProvider serverBackendProvider = new ServerBackendProvider() {
+ public void executeWorker(Runnable runable) {
+ serverManager.execute(runable);
+ }
+ public Runnable dataWorker(NetworkServer server,SocketChannel socket,byte[] data) {
+ return new NetworkServerWorker(serverManager,server,socket,data);
+ }
+ };
+ server = new NetworkServer(null, 9090, serverBackendProvider);
+ server.start();
+ }
+
+ public void stop() throws IOException {
+ server.stop();
+ }
+
+ public static void main(String[] args) {
+ try {
+ new StartServer().start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/suncertify/net/ThreadedClientTest.java b/test/suncertify/net/ThreadedClientTest.java
new file mode 100644
index 0000000..558d0e3
--- /dev/null
+++ b/test/suncertify/net/ThreadedClientTest.java
@@ -0,0 +1,147 @@
+
+
+package suncertify.net;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.net.InetAddress;
+import java.util.List;
+
+import suncertify.models.HotelRoom;
+import suncertify.server.beans.HotelRoomManagerRemote;
+import junit.framework.TestCase;
+
+/**
+ * Tests the network layer with multi threads and connections
+ *
+ * @author Willem Cazander
+ */
+public class ThreadedClientTest extends TestCase {
+
+ NetworkClient client = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ client = new NetworkClient(InetAddress.getByName("localhost"), 9090);
+ client.start();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ client.stop();
+ }
+
+ public void testSingleRequest() throws Exception {
+ final HotelRoomManagerRemote remote = (HotelRoomManagerRemote)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+ new Class[] { HotelRoomManagerRemote.class },
+ client.getInvocationHandler(HotelRoomManagerRemote.class));
+ long startTime = System.currentTimeMillis();
+ List rooms = remote.getAllHotelRooms();
+ long stopTime = System.currentTimeMillis();
+ System.out.println("got rooms1: "+rooms.size()+" from threads: "+Thread.currentThread().getName()+" in "+(stopTime-startTime)+" ms.");
+
+ if (rooms.isEmpty()) {
+ assertEquals(true,false);
+ }
+ }
+
+ public void testThreadedRequestSingleRemote() throws Exception {
+ final HotelRoomManagerRemote remote = (HotelRoomManagerRemote)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+ new Class[] { HotelRoomManagerRemote.class },
+ client.getInvocationHandler(HotelRoomManagerRemote.class));
+
+ for (int i=0;i<1;i++) {
+ Thread tt = new Thread(new Runnable() {
+ public void run() {
+ try {
+ for (int i=0;i<20000;i++) {
+ long startTime = System.currentTimeMillis();
+ List rooms = remote.getAllHotelRooms();
+ long stopTime = System.currentTimeMillis();
+ if (i%1000==0) {
+ System.out.println(i+" got rooms: "+rooms.size()+" from threads: "+Thread.currentThread().getName()+" in "+(stopTime-startTime)+" ms.");
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ tt.setPriority(10);
+ tt.setDaemon(false);
+ tt.setName("client-bench-"+i);
+ tt.start();
+ }
+
+ Thread.sleep(22000);
+ }
+
+ public void testThreadedRequestThreadRemote() throws Exception {
+ for (int i=0;i<10;i++) {
+ Thread tt = new Thread(new Runnable() {
+ public void run() {
+ try {
+ HotelRoomManagerRemote remote = (HotelRoomManagerRemote)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+ new Class[] { HotelRoomManagerRemote.class },
+ client.getInvocationHandler(HotelRoomManagerRemote.class));
+ for (int i=0;i<1000;i++) {
+ //Thread.sleep(new Random().nextInt(400) );
+ long startTime = System.currentTimeMillis();
+ List rooms = remote.getAllHotelRooms();
+ long stopTime = System.currentTimeMillis();
+ if (i%100==0) {
+ System.out.println(i+" got rooms: "+rooms.size()+" from threads: "+Thread.currentThread().getName()+" in "+(stopTime-startTime)+" ms.");
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ tt.setPriority(10);
+ tt.setDaemon(false);
+ tt.setName("client-bench-"+i);
+ tt.start();
+ }
+ Thread.sleep(22000);
+ }
+
+ public void testThreadedRequestPerThreadClient() throws Exception {
+ for (int i=0;i<10;i++) {
+ Thread tt = new Thread(new Runnable() {
+ NetworkClient clientThread = null;
+ public void run() {
+ try {
+ clientThread = new NetworkClient(InetAddress.getByName("localhost"), 9090);
+ clientThread.start();
+ HotelRoomManagerRemote remote = (HotelRoomManagerRemote)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
+ new Class[] { HotelRoomManagerRemote.class },
+ clientThread.getInvocationHandler(HotelRoomManagerRemote.class));
+ for (int i=0;i<1000;i++) {
+ //Thread.sleep(new Random().nextInt(400) );
+ long startTime = System.currentTimeMillis();
+ List rooms = remote.getAllHotelRooms();
+ long stopTime = System.currentTimeMillis();
+ if (i%100==0) {
+ System.out.println(i+" got rooms: "+rooms.size()+" from threads: "+Thread.currentThread().getName()+" in "+(stopTime-startTime)+" ms.");
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ clientThread.stop();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ });
+ tt.setPriority(10);
+ tt.setDaemon(false);
+ tt.setName("client-bench-"+i);
+ tt.start();
+ }
+ Thread.sleep(22000);
+ }
+}
\ No newline at end of file