diff options
Diffstat (limited to 'java/demo')
27 files changed, 3757 insertions, 0 deletions
diff --git a/java/demo/Database/library/BookI.java b/java/demo/Database/library/BookI.java new file mode 100644 index 00000000000..5176584b3cf --- /dev/null +++ b/java/demo/Database/library/BookI.java @@ -0,0 +1,382 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +// +// This servant is a default servant. The book identity is retrieved +// from the Ice.Current object. +// +class BookI extends _BookDisp +{ + public void + ice_ping(Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT * FROM books WHERE id = ?"); + stmt.setInt(1, id); + java.sql.ResultSet rs = stmt.executeQuery(); + if(!rs.next()) + { + throw new Ice.ObjectNotExistException(); + } + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public BookDescription + describe(Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT * FROM books WHERE id = ?"); + stmt.setInt(1, id); + java.sql.ResultSet rs = stmt.executeQuery(); + if(!rs.next()) + { + throw new Ice.ObjectNotExistException(); + } + return extractDescription(context, rs, current.adapter); + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public void + setTitle(String title, Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("UPDATE books SET title = ? WHERE id = ?"); + stmt.setString(1, title); + stmt.setInt(2, id); + int count = stmt.executeUpdate(); + assert count == 1; + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public void + setAuthors(java.util.List<String> authors, Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + + try + { + // First destroy each of the authors_books records. + java.sql.PreparedStatement stmt = context.prepareStatement("DELETE FROM authors_books WHERE book_id = ?"); + stmt.setInt(1, id); + stmt.executeUpdate(); + + // + // Convert the authors string to an id set. + // + java.util.List<Integer> authIds = new java.util.LinkedList<Integer>(); + java.util.Iterator<String> p = authors.iterator(); + while(p.hasNext()) + { + String author = p.next(); + + Integer authid; + stmt = context.prepareStatement("SELECT * FROM authors WHERE name = ?"); + stmt.setString(1, author); + java.sql.ResultSet rs = stmt.executeQuery(); + if(rs.next()) + { + // If there is a result, then the database + // already contains this author. + authid = rs.getInt(1); + assert !rs.next(); + } + else + { + // Otherwise, create a new author record. + stmt = context.prepareStatement("INSERT INTO authors (name) VALUES(?)", + java.sql.Statement.RETURN_GENERATED_KEYS); + stmt.setString(1, author); + int count = stmt.executeUpdate(); + assert count == 1; + rs = stmt.getGeneratedKeys(); + boolean next = rs.next(); + assert next; + authid = rs.getInt(1); + } + + // Add the new id to the list of ids. + authIds.add(authid); + } + + // Create new authors_books records. + java.util.Iterator<Integer> q = authIds.iterator(); + while(q.hasNext()) + { + stmt = context.prepareStatement("INSERT INTO authors_books (book_id, author_id) VALUES(?, ?)"); + stmt.setInt(1, id); + stmt.setInt(2, q.next()); + int count = stmt.executeUpdate(); + assert count == 1; + } + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public void + destroy(Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("DELETE FROM books WHERE id = ?"); + stmt.setInt(1, id); + int count = stmt.executeUpdate(); + if(count == 0) + { + throw new Ice.ObjectNotExistException(); + } + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public String + getRenter(Ice.Current current) + throws BookNotRentedException + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT * FROM books WHERE id = ?"); + stmt.setInt(1, id); + java.sql.ResultSet rs = stmt.executeQuery(); + if(!rs.next()) + { + throw new Ice.ObjectNotExistException(); + } + + int renterId = rs.getInt("renter_id"); + if(rs.wasNull()) + { + throw new BookNotRentedException(); + } + + stmt = context.prepareStatement("SELECT * FROM customers WHERE id = ?"); + stmt.setInt(1, renterId); + rs = stmt.executeQuery(); + boolean next = rs.next(); + assert next; + return rs.getString("name"); + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public void + rentBook(String name, Ice.Current current) + throws InvalidCustomerException, BookRentedException + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + name = name.trim(); + if(name.length() == 0) + { + throw new InvalidCustomerException(); + } + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT * FROM books WHERE id = ?"); + stmt.setInt(1, id); + java.sql.ResultSet rs = stmt.executeQuery(); + if(!rs.next()) + { + throw new Ice.ObjectNotExistException(); + } + + Integer renterId = rs.getInt("renter_id"); + if(!rs.wasNull()) + { + stmt = context.prepareStatement("SELECT * FROM customers WHERE id = ?"); + stmt.setInt(1, renterId); + rs = stmt.executeQuery(); + boolean next = rs.next(); + assert next; + throw new BookRentedException(rs.getString("name")); + } + + stmt = context.prepareStatement("SELECT * FROM customers WHERE name = ?"); + stmt.setString(1, name); + rs = stmt.executeQuery(); + + if(rs.next()) + { + renterId = rs.getInt("id"); + assert !rs.next(); + } + else + { + stmt = context.prepareStatement("INSERT into customers (name) VALUES(?)", + java.sql.Statement.RETURN_GENERATED_KEYS); + stmt.setString(1, name); + int count = stmt.executeUpdate(); + assert count == 1; + rs = stmt.getGeneratedKeys(); + boolean next = rs.next(); + assert next; + renterId = rs.getInt(1); + } + + stmt = context.prepareStatement("UPDATE books SET renter_id = ? WHERE id = ?"); + stmt.setInt(1, renterId); + stmt.setInt(2, id); + int count = stmt.executeUpdate(); + assert count == 1; + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public void + returnBook(Ice.Current current) + throws BookNotRentedException + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + Integer id = new Integer(current.id.name); + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT * FROM books WHERE id = ?"); + stmt.setInt(1, id); + java.sql.ResultSet rs = stmt.executeQuery(); + if(!rs.next()) + { + throw new Ice.ObjectNotExistException(); + } + Integer renterId = rs.getInt("renter_id"); + if(rs.wasNull()) + { + throw new BookNotRentedException(); + } + + stmt = context.prepareStatement("UPDATE books SET renter_id = NULL WHERE id = ?"); + stmt.setInt(1, id); + int count = stmt.executeUpdate(); + assert count == 1; + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + BookI() + { + } + + static Ice.Identity + createIdentity(Integer bookId) + { + Ice.Identity id = new Ice.Identity(); + id.category = "book"; + id.name = bookId.toString(); + return id; + } + + static BookDescription + extractDescription(SQLRequestContext context, java.sql.ResultSet rs, Ice.ObjectAdapter adapter) + throws java.sql.SQLException + { + Integer id = rs.getInt("id"); + + BookDescription desc = new BookDescription(); + desc.isbn = rs.getString("isbn"); + desc.title = rs.getString("title"); + desc.authors = new java.util.LinkedList<String>(); + desc.proxy = BookPrxHelper.uncheckedCast(adapter.createProxy(createIdentity(id))); + + java.sql.PreparedStatement stmt = null; + // Query for the rentedBy. + Integer renterId = rs.getInt("renter_id"); + if(!rs.wasNull()) + { + stmt = context.prepareStatement("SELECT * FROM customers WHERE id = ?"); + stmt.setInt(1, renterId); + java.sql.ResultSet customerRS = stmt.executeQuery(); + boolean next = customerRS.next(); + assert next; + desc.rentedBy = customerRS.getString(2); + } + + // Query for the authors. + stmt = context.prepareStatement("SELECT * FROM authors INNER JOIN authors_books ON " + + "authors.id=authors_books.author_id AND authors_books.book_id = ?"); + stmt.setInt(1, id); + java.sql.ResultSet authorRS = stmt.executeQuery(); + while(authorRS.next()) + { + desc.authors.add(authorRS.getString("name")); + } + + return desc; + } +} diff --git a/java/demo/Database/library/BookQueryResultI.java b/java/demo/Database/library/BookQueryResultI.java new file mode 100644 index 00000000000..28316ecfd5a --- /dev/null +++ b/java/demo/Database/library/BookQueryResultI.java @@ -0,0 +1,97 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class BookQueryResultI extends _BookQueryResultDisp +{ + // The query result owns the SQLRequestContext object until + // destroyed. + BookQueryResultI(SQLRequestContext context, java.sql.ResultSet rs) + { + _context = context; + _rs = rs; + } + + synchronized public java.util.List<BookDescription> + next(int n, Ice.BooleanHolder destroyed, Ice.Current current) + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + destroyed.value = false; + java.util.List<BookDescription> l = new java.util.LinkedList<BookDescription>(); + if(n <= 0) + { + return l; + } + boolean next = true; + try + { + for(int i = 0; i < n && next; ++i) + { + l.add(BookI.extractDescription(_context, _rs, current.adapter)); + next = _rs.next(); + } + } + catch(java.sql.SQLException e) + { + // Log the error, and raise an UnknownException. + _context.error("BookQueryResultI", e); + Ice.UnknownException ex = new Ice.UnknownException(); + ex.initCause(e); + throw ex; + } + + if(!next) + { + try + { + destroyed.value = true; + destroy(current); + } + catch(Exception e) + { + // Ignore. + } + } + + return l; + } + + synchronized public void + destroy(Ice.Current current) + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + _destroyed = true; + _context.destroy(false); + + current.adapter.remove(current.id); + } + + // Called on application shutdown by the Library. + synchronized public void + shutdown() + { + if(!_destroyed) + { + _destroyed = true; + _context.destroy(false); + } + } + + private SQLRequestContext _context; + private java.sql.ResultSet _rs; + private boolean _destroyed = false; +} + diff --git a/java/demo/Database/library/Client.java b/java/demo/Database/library/Client.java new file mode 100644 index 00000000000..531acd00f4a --- /dev/null +++ b/java/demo/Database/library/Client.java @@ -0,0 +1,53 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +public class Client extends Ice.Application +{ + class ShutdownHook extends Thread + { + public void + run() + { + try + { + communicator().destroy(); + } + catch(Ice.LocalException ex) + { + ex.printStackTrace(); + } + } + } + + public int + run(String[] args) + { + if(args.length > 1) + { + System.err.println("Usage: " + appName() + " [file]"); + return 1; + } + + // + // Since this is an interactive demo we want to clear the + // Application installed interrupt callback and install our + // own shutdown hook. + // + setInterruptHook(new ShutdownHook()); + + return RunParser.runParser(appName(), args, communicator()); + } + + static public void + main(String[] args) + { + Client app = new Client(); + app.main("demo.Database.library.Client", args, "config.client"); + } +} diff --git a/java/demo/Database/library/ConnectionPool.java b/java/demo/Database/library/ConnectionPool.java new file mode 100644 index 00000000000..852f6388115 --- /dev/null +++ b/java/demo/Database/library/ConnectionPool.java @@ -0,0 +1,156 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +class ConnectionPool +{ + public synchronized void + destroy() + { + _destroyed = true; + while(_connections.size() != _nconnections) + { + try + { + wait(); + } + catch(InterruptedException e) + { + } + } + + while(!_connections.isEmpty()) + { + java.sql.Connection conn = _connections.removeFirst(); + try + { + conn.close(); + } + catch(java.sql.SQLException e) + { + } + } + } + + public synchronized java.sql.Connection + acquire() + { + while(_connections.isEmpty() && !_destroyed) + { + try + { + wait(); + } + catch(InterruptedException e) + { + } + } + if(_destroyed) + { + return null; + } + java.sql.Connection conn = _connections.removeFirst(); + + try + { + boolean closed = conn.isClosed(); + if(closed) + { + _logger.warning("ConnectionPool: lost connection to database"); + conn = null; + } + else + { + // Probe the connection with the database. + java.sql.PreparedStatement stmt = conn.prepareStatement("SELECT 1"); + java.sql.ResultSet rs = stmt.executeQuery(); + stmt.close(); + } + } + catch(java.sql.SQLException e) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + e.printStackTrace(pw); + pw.flush(); + _logger.warning("ConnectionPool: lost connection to database:\n" + sw.toString()); + + conn = null; + } + + // If the connection has been closed, or is otherwise invalid, + // we need to re-establish the connection. + while(conn == null) + { + if(_trace) + { + _logger.trace("ConnectionPool", "establishing new database connection"); + } + try + { + conn = java.sql.DriverManager.getConnection(_url, _username, _password); + conn.setAutoCommit(false); + } + catch(java.sql.SQLException e) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + e.printStackTrace(pw); + pw.flush(); + _logger.warning("ConnectionPool: database connection failed:\n" + sw.toString()); + } + } + if(_trace) + { + _logger.trace("ConnectionPool", "returning connection: " + conn + " " + + _connections.size() + "/" + _nconnections + " remaining"); + } + return conn; + } + + public synchronized void + release(java.sql.Connection connection) + { + if(connection != null) + { + _connections.add(connection); + notifyAll(); + } + } + + ConnectionPool(Ice.Logger logger, String url, String username, String password, int numConnections) + throws java.sql.SQLException + { + _logger = logger; + _url = url; + _username = username; + _password = password; + + _nconnections = numConnections; + if(_trace) + { + _logger.trace("ConnectionPool", "establishing " + numConnections + " connections to " + url); + } + while(numConnections-- > 0) + { + java.sql.Connection connection = java.sql.DriverManager.getConnection(url, username, password); + connection.setAutoCommit(false); + _connections.add(connection); + } + } + + + private Ice.Logger _logger; + private boolean _trace = true; + private String _url; + private String _username; + private String _password; + private java.util.LinkedList<java.sql.Connection> _connections = new java.util.LinkedList<java.sql.Connection>(); + private boolean _destroyed = false; + private int _nconnections; +} diff --git a/java/demo/Database/library/DispatchInterceptorI.java b/java/demo/Database/library/DispatchInterceptorI.java new file mode 100644 index 00000000000..e92c9e6e73e --- /dev/null +++ b/java/demo/Database/library/DispatchInterceptorI.java @@ -0,0 +1,58 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class DispatchInterceptorI extends Ice.DispatchInterceptor +{ + public Ice.DispatchStatus + dispatch(Ice.Request request) + { + // Allocate a new SQLRequestContext associated with this + // request thread. + SQLRequestContext context = new SQLRequestContext(); + try + { + Ice.DispatchStatus status = _servant.ice_dispatch(request, null); + + // An exception causes the current transaction to rollback. + context.destroyFromDispatch(status == Ice.DispatchStatus.DispatchOK); + + return status; + } + catch(JDBCException ex) + { + // Log the error. + Ice.Current c = request.getCurrent(); + context.error("call of `" + c.operation + "' on id `" + c.id.category + "/" + c.id.name + "' failed", ex); + + // A JDBCException causes the current transaction to + // rollback. + context.destroyFromDispatch(false); + + // Translate the exception to UnknownException. + Ice.UnknownException e = new Ice.UnknownException(); + e.initCause(ex); + throw e; + } + catch(RuntimeException ex) + { + // Any other exception causes the transaction to rollback. + context.destroyFromDispatch(false); + throw ex; + } + } + + DispatchInterceptorI(Ice.Object servant) + { + _servant = servant; + } + + private Ice.Object _servant; +} diff --git a/java/demo/Database/library/Glacier2Session.ice b/java/demo/Database/library/Glacier2Session.ice new file mode 100644 index 00000000000..5a9ab2dd252 --- /dev/null +++ b/java/demo/Database/library/Glacier2Session.ice @@ -0,0 +1,50 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef LIBRARY_GLACIER2_SESSION_ICE +#define LIBRARY_GLACIER2_SESSION_ICE + +#include <Glacier2/Session.ice> + +module Demo +{ + +/* Forward declaration. */ +interface Library; + +/** + * + * The session object. This is used to retrieve a per-session library + * on behalf of the client. If the session is not refreshed on a + * periodic basis, it will be automatically destroyed. + * + */ +interface Glacier2Session extends Glacier2::Session +{ + /** + * + * Get the library object. + * + * @return A proxy for the new library. + * + **/ + Library* getLibrary(); + + /** + * + * Refresh a session. If a session is not refreshed on a regular + * basis by the client, it will be automatically destroyed. + * + **/ + idempotent void refresh(); +}; + +}; + +#endif diff --git a/java/demo/Database/library/Glacier2SessionManagerI.java b/java/demo/Database/library/Glacier2SessionManagerI.java new file mode 100644 index 00000000000..059d923de54 --- /dev/null +++ b/java/demo/Database/library/Glacier2SessionManagerI.java @@ -0,0 +1,38 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class Glacier2SessionManagerI extends Glacier2._SessionManagerDisp +{ + public Glacier2.SessionPrx + create(String userId, Glacier2.SessionControlPrx control, Ice.Current c) + { + SessionI session = new SessionI(_logger, c.adapter); + _Glacier2SessionTie servant = new _Glacier2SessionTie(session); + + Glacier2.SessionPrx proxy = Glacier2.SessionPrxHelper.uncheckedCast(c.adapter.addWithUUID(servant)); + + _logger.trace("SessionFactory", "create new session: " + + c.adapter.getCommunicator().identityToString(proxy.ice_getIdentity())); + + _reaper.add(proxy, session); + + return proxy; + } + + Glacier2SessionManagerI(Ice.Logger logger, ReapThread reaper) + { + _logger = logger; + _reaper = reaper; + } + + private Ice.Logger _logger; + private ReapThread _reaper; +} diff --git a/java/demo/Database/library/Grammar.java b/java/demo/Database/library/Grammar.java new file mode 100644 index 00000000000..5bf6a22cf95 --- /dev/null +++ b/java/demo/Database/library/Grammar.java @@ -0,0 +1,176 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +class Grammar +{ + Grammar(Parser p) + { + _parser = p; + _scanner = new Scanner(_parser); + } + + void + parse() + { + while(true) + { + try + { + _token = _scanner.nextToken(); + if(_token == null) + { + return; + } + else if(_token.type == Token.TOK_SEMI) + { + // Continue + } + else if(_token.type == Token.TOK_HELP) + { + _token = _scanner.nextToken(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + + _parser.usage(); + } + else if(_token.type == Token.TOK_EXIT) + { + _token = _scanner.nextToken(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + + return; + } + else if(_token.type == Token.TOK_ADD_BOOK) + { + java.util.List s = strings(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + _parser.addBook(s); + } + else if(_token.type == Token.TOK_FIND_ISBN) + { + java.util.List s = strings(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + _parser.findIsbn(s); + } + else if(_token.type == Token.TOK_FIND_AUTHORS) + { + java.util.List s = strings(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + _parser.findAuthors(s); + } + else if(_token.type == Token.TOK_FIND_TITLE) + { + java.util.List s = strings(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + _parser.findTitle(s); + } + else if(_token.type == Token.TOK_NEXT_FOUND_BOOK) + { + _token = _scanner.nextToken(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + + _parser.nextFoundBook(); + } + else if(_token.type == Token.TOK_PRINT_CURRENT) + { + _token = _scanner.nextToken(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + + _parser.printCurrent(); + } + else if(_token.type == Token.TOK_RENT_BOOK) + { + java.util.List s = strings(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + _parser.rentCurrent(s); + } + else if(_token.type == Token.TOK_RETURN_BOOK) + { + _token = _scanner.nextToken(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + _parser.returnCurrent(); + } + else if(_token.type == Token.TOK_REMOVE_CURRENT) + { + _token = _scanner.nextToken(); + if(_token.type != Token.TOK_SEMI) + { + throw new ParseError("Expected ';'"); + } + + _parser.removeCurrent(); + } + else + { + _parser.error("parse error"); + } + } + catch(ParseError e) + { + _parser.error("Parse error: " + e.getMessage()); + } + } + } + + private java.util.List + strings() + { + java.util.List l = new java.util.ArrayList(); + while(true) + { + _token = _scanner.nextToken(); + if(_token.type != Token.TOK_STRING) + { + return l; + } + l.add(_token.value); + } + } + + static private class ParseError extends RuntimeException + { + ParseError(String msg) + { + super(msg); + } + } + + private Parser _parser; + private Scanner _scanner; + private Token _token; +} diff --git a/java/demo/Database/library/Library.ice b/java/demo/Database/library/Library.ice new file mode 100644 index 00000000000..a26dbe20b01 --- /dev/null +++ b/java/demo/Database/library/Library.ice @@ -0,0 +1,288 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef LIBRARY_ICE +#define LIBRARY_ICE + +#include <Ice/BuiltinSequences.ice> + +module Demo +{ + +/** + * + * This local exception is used internally if a java.sql.SQLException + * is raised. + * + **/ +local exception JDBCException +{ +}; + +/** + * + * This exception is raised if the book already exists. + * + **/ +exception BookExistsException +{ +}; + +/** + * + * This exception is raised if a book has already been rented. + * + **/ +exception BookRentedException +{ + string renter; +}; + +/** + * + * This exception is raised if a customer name is invalid. + * + **/ +exception InvalidCustomerException +{ +}; + +/** + * + * This exception is raised if the book has not been rented. + * + **/ +exception BookNotRentedException +{ +}; + +/** Forward declaration for the interface Book. */ +interface Book; + +/** + * + * A description of a book. + * + **/ +struct BookDescription +{ + /** The ISBN number of the book. */ + string isbn; + + /** The title of the book. */ + string title; + + /** The authors of the book. */ + ["java:type:java.util.LinkedList<String>:java.util.List<String>"] Ice::StringSeq authors; + + /** The customer name of the renter. */ + string rentedBy; + + /** A proxy to the associated book. */ + Book* proxy; +}; + +/** A sequence of book descriptions. */ +["java:type:java.util.LinkedList<BookDescription>:java.util.List<BookDescription>"] +sequence<BookDescription> BookDescriptionSeq; + +/** + * + * This interface represents a book. + * + **/ +interface Book +{ + /** + * + * Get a description of the book. + * + * @return The book description. + * + **/ + idempotent BookDescription describe(); + + /** + * + * Set the title of a book. + * + * @param title The book title. + * + **/ + void setTitle(string title); + + /** + * + * Set the book authors. + * + * @param authors The book authors. + * + **/ + void setAuthors(["java:type:java.util.LinkedList<String>:java.util.List<String>"] Ice::StringSeq authors); + + /** + * + * Rent the book to the specified customer. + * + * @param customer The customer. + * + * @throws BookRentedException Raised if the book has already been + * rented. + * + * @throws InvalidCustomerException Raised if the customer is invalid. + * + **/ + void rentBook(string name) + throws InvalidCustomerException, BookRentedException; + + /** + * + * Get the renter. + * + * @return The current rental customer. + * + * @throws BookNotRentedException Raised if the book is not + * currently rented. + * + **/ + idempotent string getRenter() + throws BookNotRentedException; + + /** + * + * Return the book. + * + * @throws BookNotRentedException Raised if the book is not + * currently rented. + * + **/ + void returnBook() + throws BookNotRentedException; + + /** + * + * Destroy the book. + * + **/ + void destroy(); +}; + +/** + * + * Interface to get query results. + * + **/ +interface BookQueryResult +{ + /** + * + * Get more query results. + * + * @param n The maximum number of results to return. + * + * @param destroyed There are no more results, and the query has + * been destroyed. + * + * @returns A sequence of up to n results. + * + **/ + BookDescriptionSeq next(int n, out bool destroyed); + + /** + * + * Destroy the query result. + * + **/ + void destroy(); +}; + +/** + * + * An interface to the library. + * + **/ +interface Library +{ + /** + * + * Query based on isbn number. The query is a partial match at the + * start of the isbn number. + * + * @param isbn The ISBN number. + * + * @param n The number of rows to retrieve in the initial request. + + * @param first The first set of results, up to n results. + * + * @param nrows The total number of rows. + * + * @param result The remainder of the results. If there are no + * further results, a null proxy is returned. + * + **/ + void queryByIsbn(string isbn, int n, out BookDescriptionSeq first, out int nrows, out BookQueryResult* result); + + /** + * + * Query based on the author name. The query is a partial match of + * the author's name. + * + * @param author The authors name. + * + * @param n The number of rows to retrieve in the initial request. + + * @param first The first set of results, up to n results. + * + * @param nrows The total number of rows. + * + * @param result The remainder of the results. If there are no + * further results, a null proxy is returned. + * + **/ + void queryByAuthor(string author, int n, out BookDescriptionSeq first, out int nrows, out BookQueryResult* result); + + /** + * + * Query based on the book title. The query is a partial match of + * the book title. + * + * @param author The authors name. + * + * @param n The number of rows to retrieve in the initial request. + + * @param first The first set of results, up to n results. + * + * @param nrows The total number of rows. + * + * @param result The remainder of the results. If there are no + * further results, a null proxy is returned. + * + **/ + void queryByTitle(string title, int n, out BookDescriptionSeq first, out int nrows, out BookQueryResult* result); + + /** + * + * Create a book with the given description. + * + * @param description The book description. + * + * @return A proxy for the new book. + * + * @throws BookExistsException Raised if a book with the same ISBN + * number already exists. + * + **/ + Book* createBook(string isbn, string title, + ["java:type:java.util.LinkedList<String>:java.util.List<String>"] Ice::StringSeq authors) + throws BookExistsException; +}; + +}; + +#endif diff --git a/java/demo/Database/library/LibraryI.java b/java/demo/Database/library/LibraryI.java new file mode 100644 index 00000000000..81bae5c6de7 --- /dev/null +++ b/java/demo/Database/library/LibraryI.java @@ -0,0 +1,387 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +// +// This is a per-session library object. +// +class LibraryI extends _LibraryDisp +{ + public void + queryByIsbn(String isbn, int n, BookDescriptionSeqHolder first, Ice.IntHolder nrows, + BookQueryResultPrxHolder result, Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + + reapQueries(); + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT COUNT(*) FROM books WHERE isbn LIKE ?"); + stmt.setString(1, isbn + "%"); + java.sql.ResultSet rs = stmt.executeQuery(); + boolean next = rs.next(); + assert next; + nrows.value = rs.getInt(1); + if(nrows.value == 0) + { + return; + } + + stmt = context.prepareStatement("SELECT * FROM books WHERE isbn LIKE ?"); + stmt.setString(1, isbn + "%"); + rs = stmt.executeQuery(); + next = rs.next(); + assert next; + + first.value = new java.util.LinkedList<BookDescription>(); + next = true; + for(int i = 0; i < n && next; ++i) + { + first.value.add(BookI.extractDescription(context, rs, current.adapter)); + next = rs.next(); + } + if(next) + { + // The SQLRequestContext is now owned by the query + // implementation. + context.obtain(); + BookQueryResultI impl = new BookQueryResultI(context, rs); + result.value = BookQueryResultPrxHelper.uncheckedCast(current.adapter.addWithUUID(impl)); + add(result.value, impl); + } + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public void + queryByAuthor(String author, int n, BookDescriptionSeqHolder first, Ice.IntHolder nrows, + BookQueryResultPrxHolder result, Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + + reapQueries(); + + try + { + // Find each of the authors. + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT * FROM authors WHERE name LIKE ?"); + stmt.setString(1, "%" + author + "%"); + java.sql.ResultSet rs = stmt.executeQuery(); + if(!rs.next()) + { + // No results are available. + nrows.value = 0; + return; + } + + // Build a query that finds all books by these authors. + StringBuffer sb = new StringBuffer("("); + boolean front = true; + do + { + if(!front) + { + sb.append(" OR "); + } + front = false; + sb.append("authors_books.author_id="); + sb.append(rs.getInt("id")); + } + while(rs.next()); + sb.append(")"); + + stmt = context.prepareStatement( + "SELECT COUNT(DISTINCT ID) FROM books INNER JOIN authors_books ON books.id=authors_books.book_id AND " + + sb.toString()); + rs = stmt.executeQuery(); + boolean next = rs.next(); + assert next; + nrows.value = rs.getInt(1); + if(nrows.value == 0) + { + return; + } + + // Execute the query. + stmt = context.prepareStatement( + "SELECT DISTINCT ID, ISBN, TITLE, RENTER_ID FROM books INNER JOIN authors_books ON " + + "books.id=authors_books.book_id AND " + sb.toString()); + rs = stmt.executeQuery(); + next = rs.next(); + assert next; + + next = true; + first.value = new java.util.LinkedList<BookDescription>(); + for(int i = 0; i < n && next; ++i) + { + first.value.add(BookI.extractDescription(context, rs, current.adapter)); + next = rs.next(); + } + if(next) + { + // The SQLRequestContext is now owned by the query + // implementation. + context.obtain(); + BookQueryResultI impl = new BookQueryResultI(context, rs); + result.value = BookQueryResultPrxHelper.uncheckedCast(current.adapter.addWithUUID(impl)); + add(result.value, impl); + } + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public void + queryByTitle(String title, int n, BookDescriptionSeqHolder first, Ice.IntHolder nrows, + BookQueryResultPrxHolder result, Ice.Current current) + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + + reapQueries(); + + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT COUNT(*) FROM books WHERE title LIKE ?"); + stmt.setString(1, "%" + title + "%"); + java.sql.ResultSet rs = stmt.executeQuery(); + boolean next = rs.next(); + assert next; + nrows.value = rs.getInt(1); + if(nrows.value == 0) + { + return; + } + + stmt = context.prepareStatement("SELECT * FROM books WHERE title LIKE ?"); + stmt.setString(1, "%" + title + "%"); + rs = stmt.executeQuery(); + next = rs.next(); + assert next; + + first.value = new java.util.LinkedList<BookDescription>(); + next = true; + for(int i = 0; i < n && next; ++i) + { + first.value.add(BookI.extractDescription(context, rs, current.adapter)); + next = rs.next(); + } + if(next) + { + // The SQLRequestContext is now owned by the query + // implementation. + context.obtain(); + BookQueryResultI impl = new BookQueryResultI(context, rs); + result.value = BookQueryResultPrxHelper.uncheckedCast(current.adapter.addWithUUID(impl)); + add(result.value, impl); + } + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + public BookPrx + createBook(String isbn, String title, java.util.List<String> authors, Ice.Current current) + throws BookExistsException + { + SQLRequestContext context = SQLRequestContext.getCurrentContext(); + assert context != null; + try + { + java.sql.PreparedStatement stmt = context.prepareStatement("SELECT * FROM books WHERE isbn = ?"); + stmt.setString(1, isbn); + java.sql.ResultSet rs = stmt.executeQuery(); + if(rs.next()) + { + throw new BookExistsException(); + } + + // + // First convert the authors string to an id set. + // + java.util.List<Integer> authIds = new java.util.LinkedList<Integer>(); + java.util.Iterator<String> p = authors.iterator(); + while(p.hasNext()) + { + String author = p.next(); + + Integer id; + stmt = context.prepareStatement("SELECT * FROM authors WHERE name = ?"); + stmt.setString(1, author); + rs = stmt.executeQuery(); + if(rs.next()) + { + // If there is a result, then the database + // already contains this author. + id = rs.getInt(1); + assert !rs.next(); + } + else + { + // Otherwise, create a new author record. + stmt = context.prepareStatement("INSERT INTO authors (name) VALUES(?)", + java.sql.Statement.RETURN_GENERATED_KEYS); + stmt.setString(1, author); + int count = stmt.executeUpdate(); + assert count == 1; + rs = stmt.getGeneratedKeys(); + boolean next = rs.next(); + assert next; + id = rs.getInt(1); + } + + // Add the new id to the list of ids. + authIds.add(id); + } + + // Create the new book. + stmt = context.prepareStatement("INSERT INTO books (isbn, title) VALUES(?, ?)", + java.sql.Statement.RETURN_GENERATED_KEYS); + stmt.setString(1, isbn); + stmt.setString(2, title); + int count = stmt.executeUpdate(); + assert count == 1; + + rs = stmt.getGeneratedKeys(); + boolean next = rs.next(); + assert next; + Integer bookId = rs.getInt(1); + + // Create new authors_books records. + java.util.Iterator<Integer> q = authIds.iterator(); + while(q.hasNext()) + { + stmt = context.prepareStatement("INSERT INTO authors_books (book_id, author_id) VALUES(?, ?)"); + stmt.setInt(1, bookId); + stmt.setInt(2, q.next()); + count = stmt.executeUpdate(); + assert count == 1; + } + + return BookPrxHelper.uncheckedCast(current.adapter.createProxy(BookI.createIdentity(bookId))); + } + catch(java.sql.SQLException e) + { + JDBCException ex = new JDBCException(); + ex.initCause(e); + throw ex; + } + } + + LibraryI() + { + } + + synchronized public void + destroy() + { + if(_destroyed) + { + return; + } + _destroyed = true; + java.util.Iterator<QueryProxyPair> p = _queries.iterator(); + while(p.hasNext()) + { + try + { + p.next().proxy.destroy(); + } + catch(Ice.ObjectNotExistException e) + { + // Ignore, it could have already been destroyed. + } + } + } + + synchronized public void + shutdown() + { + if(_destroyed) + { + return; + } + _destroyed = true; + + // Shutdown each of the associated query objects. + java.util.Iterator<QueryProxyPair> p = _queries.iterator(); + while(p.hasNext()) + { + p.next().impl.shutdown(); + } + } + + synchronized private void + add(BookQueryResultPrx proxy, BookQueryResultI impl) + { + // If the session has been destroyed, then destroy the book + // result, and raise an ObjectNotExistException. + if(_destroyed) + { + proxy.destroy(); + throw new Ice.ObjectNotExistException(); + } + _queries.add(new QueryProxyPair(proxy, impl)); + } + + synchronized private void + reapQueries() + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + + java.util.Iterator<QueryProxyPair> p = _queries.iterator(); + while(p.hasNext()) + { + QueryProxyPair pair = p.next(); + try + { + pair.proxy.ice_ping(); + } + catch(Ice.ObjectNotExistException e) + { + p.remove(); + } + } + } + + static class QueryProxyPair + { + QueryProxyPair(BookQueryResultPrx p, BookQueryResultI i) + { + proxy = p; + impl = i; + } + + BookQueryResultPrx proxy; + BookQueryResultI impl; + } + + private java.util.List<QueryProxyPair> _queries = new java.util.LinkedList<QueryProxyPair>(); + private boolean _destroyed = false; +} diff --git a/java/demo/Database/library/Parser.java b/java/demo/Database/library/Parser.java new file mode 100644 index 00000000000..2b2b8cb71fd --- /dev/null +++ b/java/demo/Database/library/Parser.java @@ -0,0 +1,443 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class Parser +{ + Parser(Ice.Communicator communicator, LibraryPrx library) + { + _library = library; + } + + void + usage() + { + System.err.print( + "help Print this message.\n" + + "exit, quit Exit this program.\n" + + "add isbn title authors Create new book.\n" + + "isbn NUMBER Find all books that start with the given ISBN number.\n" + + "authors NAME Find all books by the given authors.\n" + + "title NAME Find all books which have the given title.\n" + + "next Set the current book to the next one that was found.\n" + + "current Display the current book.\n" + + "rent NAME Rent the current book for customer NAME.\n" + + "return Return the currently rented book.\n" + + "remove Permanently remove the current book from the library.\n"); + } + + void + addBook(java.util.List args) + { + if(args.size() != 3) + { + error("`add' requires exactly three arguments (type `help' for more info)"); + return; + } + + try + { + String isbn = (String)args.get(0); + String title = (String)args.get(1); + + java.util.List<String> authors = new java.util.LinkedList<String>(); + java.util.StringTokenizer st = new java.util.StringTokenizer((String)args.get(2), ","); + while(st.hasMoreTokens()) + { + authors.add(st.nextToken().trim()); + } + + BookPrx book = _library.createBook(isbn, title, authors); + System.out.println("added new book with isbn " + isbn); + } + catch(BookExistsException ex) + { + error("the book already exists"); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + findIsbn(java.util.List args) + { + if(args.size() != 1) + { + error("`isbn' requires exactly one argument (type `help' for more info)"); + return; + } + + try + { + if(_query != null) + { + try + { + _query.destroy(); + } + catch(Exception e) + { + // Ignore + } + _query = null; + _current = null; + } + + BookDescriptionSeqHolder first = new BookDescriptionSeqHolder(); + Ice.IntHolder nrows = new Ice.IntHolder(); + BookQueryResultPrxHolder result = new BookQueryResultPrxHolder(); + _library.queryByIsbn((String)args.get(0), 1, first, nrows, result); + + System.out.println(nrows.value + " results"); + if(nrows.value == 0) + { + return; + } + + _current = first.value.get(0); + _query = result.value; + printCurrent(); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + findAuthors(java.util.List args) + { + if(args.size() != 1) + { + error("`authors' requires exactly one argument (type `help' for more info)"); + return; + } + + try + { + if(_query != null) + { + try + { + _query.destroy(); + } + catch(Exception e) + { + // Ignore + } + _query = null; + _current = null; + } + + BookDescriptionSeqHolder first = new BookDescriptionSeqHolder(); + Ice.IntHolder nrows = new Ice.IntHolder(); + BookQueryResultPrxHolder result = new BookQueryResultPrxHolder(); + _library.queryByAuthor((String)args.get(0), 1, first, nrows, result); + + System.out.println(nrows.value + " results"); + if(nrows.value == 0) + { + return; + } + + _current = first.value.get(0); + _query = result.value; + printCurrent(); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + findTitle(java.util.List args) + { + if(args.size() != 1) + { + error("`title' requires exactly one argument (type `help' for more info)"); + return; + } + + try + { + if(_query != null) + { + try + { + _query.destroy(); + } + catch(Exception e) + { + // Ignore + } + _query = null; + _current = null; + } + + BookDescriptionSeqHolder first = new BookDescriptionSeqHolder(); + Ice.IntHolder nrows = new Ice.IntHolder(); + BookQueryResultPrxHolder result = new BookQueryResultPrxHolder(); + _library.queryByTitle((String)args.get(0), 1, first, nrows, result); + + System.out.println(nrows.value + " results"); + if(nrows.value == 0) + { + return; + } + + _current = first.value.get(0); + _query = result.value; + printCurrent(); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + nextFoundBook() + { + if(_query == null) + { + System.out.println("no next book"); + return; + } + + try + { + Ice.BooleanHolder destroyed = new Ice.BooleanHolder(); + java.util.List<BookDescription> next = _query.next(1, destroyed); + if(next.size() > 0) + { + _current = next.get(0); + } + else + { + assert destroyed.value; + _current = null; + } + if(destroyed.value) + { + _query = null; + } + printCurrent(); + } + catch(Ice.ObjectNotExistException ex) + { + System.out.println("the query object no longer exists"); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + printCurrent() + { + if(_current != null) + { + System.out.println("current book is:"); + System.out.println("isbn: " + _current.isbn); + System.out.println("title: " + _current.title); + System.out.println("authors: " + _current.authors); + if(_current.rentedBy.length() > 0) + { + System.out.println("rented: " + _current.rentedBy); + } + } + else + { + System.out.println("no current book"); + } + } + + void + rentCurrent(java.util.List args) + { + if(args.size() != 1) + { + error("`rent' requires exactly one argument (type `help' for more info)"); + return; + } + + try + { + if(_current != null) + { + _current.proxy.rentBook((String)args.get(0)); + System.out.println("the book is now rented by `" + (String)args.get(0) + "'"); + _current = _current.proxy.describe(); + } + else + { + System.out.println("no current book"); + } + } + catch(BookRentedException ex) + { + System.out.println("the book has already been rented"); + } + catch(InvalidCustomerException ex) + { + System.out.println("the customer name is invalid"); + } + catch(Ice.ObjectNotExistException ex) + { + System.out.println("current book no longer exists"); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + returnCurrent() + { + try + { + if(_current != null) + { + _current.proxy.returnBook(); + System.out.println( "the book has been returned"); + _current = _current.proxy.describe(); + } + else + { + System.out.println("no current book"); + } + } + catch(BookNotRentedException ex) + { + System.out.println("the book is not currently rented"); + } + catch(Ice.ObjectNotExistException ex) + { + System.out.println("current book no longer exists"); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + removeCurrent() + { + try + { + if(_current != null) + { + _current.proxy.destroy(); + _current = null; + System.out.println("removed current book" ); + } + else + { + System.out.println("no current book" ); + } + } + catch(Ice.ObjectNotExistException ex) + { + System.out.println("current book no longer exists"); + } + catch(Ice.LocalException ex) + { + error(ex.toString()); + } + } + + void + error(String s) + { + System.err.println("error: " + s); + } + + void + warning(String s) + { + System.err.println("warning: " + s); + } + + String + getInput() + { + System.out.print(">>> "); + System.out.flush(); + + try + { + return _in.readLine(); + } + catch(java.io.IOException e) + { + return null; + } + } + + int + parse() + { + _query = null; + _current = null; + + _in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); + + Grammar g = new Grammar(this); + g.parse(); + + return 0; + } + + int + parse(String file) + { + _query = null; + _current = null; + + try + { + _in = new java.io.BufferedReader(new java.io.FileReader(file)); + } + catch(java.io.FileNotFoundException ex) + { + error(ex.getMessage()); + return 1; + } + + Grammar g = new Grammar(this); + g.parse(); + + System.out.println(); + + try + { + _in.close(); + } + catch(java.io.IOException ex) + { + } + + return 0; + } + + private BookQueryResultPrx _query; + private BookDescription _current; + + private LibraryPrx _library; + + private java.io.BufferedReader _in; + private boolean _interactive; +} diff --git a/java/demo/Database/library/README b/java/demo/Database/library/README new file mode 100644 index 00000000000..b7e44a0dbdb --- /dev/null +++ b/java/demo/Database/library/README @@ -0,0 +1,103 @@ +MySQL JDBC Demo +=============== + +This demo shows how to implement an Ice server that uses mysql through +a JDBC API and demonstrates the following techniques: + + - Mapping relational data to Ice objects, and in particular the + conversion between Ice and JDBC types. + - Using a JDBC connection pool to provide JDBC connections for Ice + requests. + - Using an Ice servant locator. + - Using a dispatch interceptor. + + +Building the demo +----------------- + +1. Install mysql if necessary. + +2. Download version 5.0.8 of the mysql JDBC connector here: + + http://dev.mysql.com/downloads/connector/j/5.0.html + + After extracting the archive, add mysql-connector-java-5.0.8-bin.jar + to your CLASSPATH. + +3. Create a database named "library" and grant privileges to a user. In + the commands below, replace USER with the name you have chosen and + PASSWORD with a suitable password: + + $ mysql -u root -p + Enter password: + Welcome to the MySQL monitor. + + mysql> CREATE DATABASE library; + Query OK, 1 row affected (0.00 sec) + + mysql> GRANT ALL PRIVILEGES ON library.* TO "USER"@"localhost" + -> IDENTIFIED BY "PASSWORD"; + Query OK, 0 rows affected (0.00 sec) + + mysql> FLUSH PRIVILEGES; + Query OK, 0 rows affected (0.01 sec) + + mysql> EXIT + +4. Create the SQL tables using the script createTypes.sql: + + $ mysql --user=USER --pass=PASSWORD library < createTypes.sql + +5. Edit the JDBC properties in config.server to reflect your selected + user name and password: + + JDBC.Username=USER + JDBC.Password=PASSWORD + +NOTE: The instructions assume that the demo server runs on the same + host as the mysql server. If you intend to run the demo server on + a different host than the mysql server, you will need to revise + the mysql privileges as well as the JDBC URL in config.server. + + +Running the demo +---------------- + +To run the demo, first start the server: + +$ java Server + +The demo includes a text file named "books" containing a series of +commands that populate the server's database with a collection of +books. Pass this file as an argument the first time you run the +client. In another window: + +$ java Client books + +Type "help" to get a list of valid commands. + + +Running the demo with Glacier2 +------------------------------ + +The demo also supports a Glacier2 deployment. You will need to edit +config.client and uncomment these configuration parameters: + +#Ice.Default.Router=DemoGlacier2/router:ssl -p 4064 -h 127.0.0.1 +#Ice.ACM.Client=0 +#Ice.RetryIntervals=-1 + +To run the demo using Glacier2, first start the server: + +$ java Server + +In a separate window, start the Glacier2 router: + +$ glacier2router --Ice.Config=config.glacier2 + +In a separate window, start the client: + +$ java Client books + +Omit the "books" argument if you have already populated the server's +database. diff --git a/java/demo/Database/library/ReapThread.java b/java/demo/Database/library/ReapThread.java new file mode 100644 index 00000000000..b44a79b6e8b --- /dev/null +++ b/java/demo/Database/library/ReapThread.java @@ -0,0 +1,127 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class ReapThread extends Thread +{ + static class SessionProxyPair + { + SessionProxyPair(Demo.SessionPrx p, SessionI s) + { + glacier2proxy = null; + proxy = p; + session = s; + } + + SessionProxyPair(Glacier2.SessionPrx p, SessionI s) + { + glacier2proxy = p; + proxy = null; + session = s; + } + + Glacier2.SessionPrx glacier2proxy; + Demo.SessionPrx proxy; + SessionI session; + } + + ReapThread(Ice.Logger logger, long timeout) + { + _logger = logger; + _timeout = timeout; + } + + synchronized public void + run() + { + while(!_terminated) + { + try + { + wait((_timeout / 2) * 1000); + } + catch(InterruptedException e) + { + } + + if(!_terminated) + { + java.util.Iterator<SessionProxyPair> p = _sessions.iterator(); + while(p.hasNext()) + { + SessionProxyPair s = p.next(); + try + { + // + // Session destruction may take time in a + // real-world example. Therefore the current time + // is computed for each iteration. + // + if((System.currentTimeMillis() - s.session.timestamp()) > _timeout * 1000) + { + _logger.trace("ReapThread", "The session " + + s.proxy.ice_getCommunicator().identityToString(s.proxy.ice_getIdentity()) + + " has timed out."); + if(s.proxy != null) + { + s.proxy.destroy(); + } + else + { + s.glacier2proxy.destroy(); + } + p.remove(); + } + } + catch(Ice.ObjectNotExistException e) + { + p.remove(); + } + } + } + } + } + + synchronized public void + terminate() + { + _terminated = true; + notify(); + + // Destroy each of the sessions, releasing any resources they + // may hold. This calls directly on the session, not via the + // proxy since terminate() is called after the communicator is + // shutdown, which means calls on collocated objects are not + // permitted. + java.util.Iterator<SessionProxyPair> p = _sessions.iterator(); + while(p.hasNext()) + { + p.next().session.shutdown(); + } + _sessions.clear(); + } + + synchronized public void + add(SessionPrx proxy, SessionI session) + { + _sessions.add(new SessionProxyPair(proxy, session)); + } + + synchronized public void + add(Glacier2.SessionPrx proxy, SessionI session) + { + _sessions.add(new SessionProxyPair(proxy, session)); + } + + private final long _timeout; // Seconds. + private Ice.Logger _logger; + private boolean _terminated = false; + private java.util.List<SessionProxyPair> _sessions = new java.util.LinkedList<SessionProxyPair>(); +} diff --git a/java/demo/Database/library/RunParser.java b/java/demo/Database/library/RunParser.java new file mode 100644 index 00000000000..e616218df9e --- /dev/null +++ b/java/demo/Database/library/RunParser.java @@ -0,0 +1,220 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class RunParser +{ + // + // Adapter for the two types of session objects. + // + interface SessionAdapter + { + public LibraryPrx getLibrary(); + public void destroy(); + public void refresh(); + } + + static class Glacier2SessionAdapter implements SessionAdapter + { + public LibraryPrx getLibrary() + { + return _session.getLibrary(); + } + + public void destroy() + { + _session.destroy(); + } + + public void refresh() + { + _session.refresh(); + } + + Glacier2SessionAdapter(Glacier2SessionPrx session) + { + _session = session; + } + + private Glacier2SessionPrx _session; + } + + static class DemoSessionAdapter implements SessionAdapter + { + public LibraryPrx getLibrary() + { + return _session.getLibrary(); + } + + public void destroy() + { + _session.destroy(); + } + + public void refresh() + { + _session.refresh(); + } + + DemoSessionAdapter(SessionPrx session) + { + _session = session; + } + + private SessionPrx _session; + } + + static private class SessionRefreshThread extends Thread + { + SessionRefreshThread(Ice.Logger logger, long timeout, SessionAdapter session) + { + _logger = logger; + _session = session; + _timeout = timeout; // seconds. + } + + synchronized public void + run() + { + while(!_terminated) + { + try + { + wait(_timeout * 1000); + } + catch(InterruptedException e) + { + } + if(!_terminated) + { + try + { + _session.refresh(); + } + catch(Ice.LocalException ex) + { + _logger.warning("SessionRefreshThread: " + ex); + _terminated = true; + } + } + } + } + + synchronized private void + terminate() + { + _terminated = true; + notify(); + } + + final private Ice.Logger _logger; + final private SessionAdapter _session; + final private long _timeout; + private boolean _terminated = false; + } + + static int + runParser(String appName, String[] args, Ice.Communicator communicator) + { + SessionAdapter session; + Glacier2.RouterPrx router = Glacier2.RouterPrxHelper.uncheckedCast(communicator.getDefaultRouter()); + long timeout; + if(router != null) + { + Glacier2.SessionPrx glacier2session = null; + java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); + while(true) + { + System.out.println("This demo accepts any user-id / password combination."); + + try + { + String id; + System.out.print("user id: "); + System.out.flush(); + id = in.readLine(); + + String pw; + System.out.print("password: "); + System.out.flush(); + pw = in.readLine(); + + try + { + glacier2session = router.createSession(id, pw); + timeout = router.getSessionTimeout() / 2; + break; + } + catch(Glacier2.PermissionDeniedException ex) + { + System.out.println("permission denied:\n" + ex.reason); + } + catch(Glacier2.CannotCreateSessionException ex) + { + System.out.println("cannot create session:\n" + ex.reason); + } + } + catch(java.io.IOException ex) + { + ex.printStackTrace(); + } + } + session = new Glacier2SessionAdapter(Glacier2SessionPrxHelper.uncheckedCast(glacier2session)); + } + else + { + SessionFactoryPrx factory = SessionFactoryPrxHelper.checkedCast( + communicator.propertyToProxy("SessionFactory.Proxy")); + if(factory == null) + { + System.err.println(appName + ": invalid object reference"); + return 1; + } + + session = new DemoSessionAdapter(factory.create()); + timeout = factory.getSessionTimeout()/2; + } + SessionRefreshThread refresh = new SessionRefreshThread(communicator.getLogger(), timeout, session); + refresh.start(); + + LibraryPrx library = session.getLibrary(); + + Parser parser = new Parser(communicator, library); + + int rc = 0; + + if(args.length == 1) + { + rc = parser.parse(args[0]); + } + + if(rc == 0) + { + rc = parser.parse(); + } + + if(refresh != null) + { + refresh.terminate(); + try + { + refresh.join(); + } + catch(InterruptedException e) + { + } + refresh = null; + } + + session.destroy(); + + return rc; + } +} diff --git a/java/demo/Database/library/SQLRequestContext.java b/java/demo/Database/library/SQLRequestContext.java new file mode 100644 index 00000000000..533e74be491 --- /dev/null +++ b/java/demo/Database/library/SQLRequestContext.java @@ -0,0 +1,181 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// A SQL request context encapsulates SQL resources allocated in the +// process of executing a request, such as the database connection, +// and associated SQL statements. +// +// The request context is automatically destroyed at the end of a +// request, or if obtain is called it must be destroyed manually by +// calling destroy. +// +// When the request context is destroyed, the transaction is either +// automatically committed or rolled back, depending whether the +// request executed successfully. +// +class SQLRequestContext +{ + public static SQLRequestContext + getCurrentContext() + { + synchronized(_contextMap) + { + return _contextMap.get(Thread.currentThread()); + } + } + + public static void + initialize(Ice.Logger logger, ConnectionPool pool) + { + assert _logger == null; + assert _pool == null; + + _logger = logger; + _pool = pool; + } + + public java.sql.PreparedStatement + prepareStatement(String sql) + throws java.sql.SQLException + { + java.sql.PreparedStatement stmt = _conn.prepareStatement(sql); + _statements.add(stmt); + return stmt; + } + + public java.sql.PreparedStatement + prepareStatement(String sql, int autoGeneratedKeys) + throws java.sql.SQLException + { + java.sql.PreparedStatement stmt = _conn.prepareStatement(sql, autoGeneratedKeys); + _statements.add(stmt); + return stmt; + } + + // Called to obtain ownership of the context. The context is no + // longer destroyed automatically when the current request has + // completed. + public void + obtain() + { + if(_trace) + { + _logger.trace("SQLRequestContext", "obtain context: " + this + + " thread: " + Thread.currentThread()); + } + _obtain = true; + } + + public void + destroy(boolean commit) + { + // Must only be called on an obtained context. + assert _obtain; + destroyInternal(commit); + } + + public void + error(String prefix, Exception ex) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + ex.printStackTrace(pw); + pw.flush(); + _logger.error(prefix + ": error:\n" + sw.toString()); + } + + SQLRequestContext() + { + _conn = _pool.acquire(); + + synchronized(_contextMap) + { + if(_trace) + { + _logger.trace("SQLRequestContext", "create new context: " + this + + " thread: " + Thread.currentThread() + + ": connection: " + _conn); + } + + _contextMap.put(Thread.currentThread(), this); + } + } + + // Called only during the dispatch process. + void + destroyFromDispatch(boolean commit) + { + synchronized(_contextMap) + { + // Remove the current context from the thread->context + // map. + SQLRequestContext context = _contextMap.remove(Thread.currentThread()); + assert context != null; + } + + // If the context was obtained then don't destroy. + if(!_obtain) + { + destroyInternal(commit); + } + } + + private void + destroyInternal(boolean commit) + { + // Release all resources. + try + { + if(commit) + { + _conn.commit(); + if(_trace) + { + _logger.trace("SQLRequestContext", "commit context: " + this); + } + } + else + { + _conn.rollback(); + if(_trace) + { + _logger.trace("SQLRequestContext", "rollback context: " + this); + } + } + + java.util.Iterator<java.sql.Statement> p = _statements.iterator(); + while(p.hasNext()) + { + p.next().close(); + } + } + catch(java.sql.SQLException e) + { + error("SQLRequestContext", e); + } + + _pool.release(_conn); + + _statements.clear(); + _conn = null; + } + + // A map of threads to request contexts. + private static java.util.Map<Thread, SQLRequestContext> _contextMap = + new java.util.HashMap<Thread, SQLRequestContext>(); + + private static Ice.Logger _logger = null; + private static ConnectionPool _pool = null; + + private boolean _trace = true; + private java.util.List<java.sql.Statement> _statements = new java.util.LinkedList<java.sql.Statement>(); + private java.sql.Connection _conn; + private boolean _obtain = false; +} diff --git a/java/demo/Database/library/Scanner.java b/java/demo/Database/library/Scanner.java new file mode 100644 index 00000000000..879396725b4 --- /dev/null +++ b/java/demo/Database/library/Scanner.java @@ -0,0 +1,283 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +class Scanner +{ + Scanner(Parser p) + { + _parser = p; + } + + Token + nextToken() + { + String s = next(); + if(s == null) + { + return null; + } + + if(s.equals(";")) + { + return new Token(Token.TOK_SEMI); + } + else if(s.equals("help")) + { + return new Token(Token.TOK_HELP); + } + else if(s.equals("exit") || s.equals("quit")) + { + return new Token(Token.TOK_EXIT); + } + else if(s.equals("add")) + { + return new Token(Token.TOK_ADD_BOOK); + } + else if(s.equals("isbn")) + { + return new Token(Token.TOK_FIND_ISBN); + } + else if(s.equals("authors")) + { + return new Token(Token.TOK_FIND_AUTHORS); + } + else if(s.equals("title")) + { + return new Token(Token.TOK_FIND_TITLE); + } + else if(s.equals("next")) + { + return new Token(Token.TOK_NEXT_FOUND_BOOK); + } + else if(s.equals("current")) + { + return new Token(Token.TOK_PRINT_CURRENT); + } + else if(s.equals("rent")) + { + return new Token(Token.TOK_RENT_BOOK); + } + else if(s.equals("return")) + { + return new Token(Token.TOK_RETURN_BOOK); + } + else if(s.equals("remove")) + { + return new Token(Token.TOK_REMOVE_CURRENT); + } + else + { + return new Token(Token.TOK_STRING, s); + } + } + + static private class EndOfInput extends Exception + { + } + + private char + get() + throws EndOfInput + { + // + // If there is an character in the unget buffer, return it. + // + if(_unget) + { + _unget = false; + return _ungetChar; + } + + // + // No current buffer? + // + if(_buf == null) + { + _buf = _parser.getInput(); + _pos = 0; + if(_buf == null) + { + throw new EndOfInput(); + } + } + + // + // At the end-of-buffer? + // + while(_pos >= _buf.length()) + { + _buf = null; + _pos = 0; + return '\n'; + } + + return _buf.charAt(_pos++); + } + + // + // unget only works with one character. + // + private void + unget(char c) + { + assert(!_unget); + _unget = true; + _ungetChar = c; + } + + private String + next() + { + // + // Eat any whitespace. + // + char c; + try + { + do + { + c = get(); + } + while(Character.isWhitespace(c) && c != '\n'); + } + catch(EndOfInput ignore) + { + return null; + } + + StringBuffer buf = new StringBuffer(); + + if(c == ';' || c == '\n') + { + buf.append(';'); + } + else if(c == '\'') + { + try + { + while(true) + { + c = get(); + if(c == '\'') + { + break; + } + else + { + buf.append(c); + } + } + } + catch(EndOfInput e) + { + _parser.warning("EOF in string"); + } + } + else if(c == '\"') + { + try + { + while(true) + { + c = get(); + if(c == '\"') + { + break; + } + else if(c == '\\') + { + try + { + char next = get(); + switch(next) + { + case '\\': + case '"': + { + buf.append(next); + break; + } + + case 'n': + { + buf.append('\n'); + break; + } + + case 'r': + { + buf.append('\r'); + break; + } + + case 't': + { + buf.append('\t'); + break; + } + + case 'f': + { + buf.append('\f'); + break; + } + + default: + { + buf.append(c); + unget(next); + } + } + } + catch(EndOfInput e) + { + buf.append(c); + } + } + else + { + buf.append(c); + } + } + } + catch(EndOfInput e) + { + _parser.warning("EOF in string"); + } + } + else + { + // + // Otherwise it's a string. + // + try + { + do + { + buf.append(c); + c = get(); + } + while(!Character.isWhitespace(c) && c != ';' && c != '\n'); + + unget(c); + } + catch(EndOfInput ignore) + { + } + } + + return buf.toString(); + } + + private Parser _parser; + private boolean _unget = false; + private char _ungetChar; + private String _buf = null; + private int _pos; +} diff --git a/java/demo/Database/library/Server.java b/java/demo/Database/library/Server.java new file mode 100644 index 00000000000..b2e66386ffa --- /dev/null +++ b/java/demo/Database/library/Server.java @@ -0,0 +1,142 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class LibraryServer extends Ice.Application +{ + static class LocatorI implements Ice.ServantLocator + { + public Ice.Object + locate(Ice.Current c, Ice.LocalObjectHolder cookie) + { + assert c.id.category.equals("book"); + return _servant; + } + + public void + finished(Ice.Current c, Ice.Object servant, Object cookie) + { + } + + public void + deactivate(String category) + { + } + + LocatorI(Ice.Object servant) + { + _servant = new DispatchInterceptorI(servant); + } + + private Ice.Object _servant; + } + + public int + run(String[] args) + { + args = communicator().getProperties().parseCommandLineOptions("JDBC", args); + + if(args.length > 0) + { + System.err.println(appName() + ": too many arguments"); + return 1; + } + + Ice.Properties properties = communicator().getProperties(); + + String username = properties.getProperty("JDBC.Username"); + String password = properties.getProperty("JDBC.Password"); + String url = properties.getProperty("JDBC.Url"); + int nConnections = properties.getPropertyAsIntWithDefault("JDBC.NumConnections", 5); + if(nConnections < 1) + { + nConnections = 1; + } + ConnectionPool pool = null; + Ice.Logger logger = communicator().getLogger(); + + try + { + Class.forName("com.mysql.jdbc.Driver").newInstance(); + } + catch(Exception e) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + e.printStackTrace(pw); + pw.flush(); + System.err.println("failed to initialize mysql driver:\n" + sw.toString()); + return 1; + } + + try + { + pool = new ConnectionPool(logger, url, username, password, nConnections); + } + catch(java.sql.SQLException e) + { + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + e.printStackTrace(pw); + pw.flush(); + System.err.println("failed to create connection pool: SQLException:\n" + sw.toString()); + return 1; + } + + long timeout = properties.getPropertyAsIntWithDefault("SessionTimeout", 30); + + ReapThread reaper = new ReapThread(logger, timeout); + reaper.start(); + + // + // Create an object adapter + // + Ice.ObjectAdapter adapter = communicator().createObjectAdapter("SessionFactory"); + + SQLRequestContext.initialize(logger, pool); + adapter.addServantLocator(new LocatorI(new BookI()), "book"); + + adapter.add(new SessionFactoryI(logger, reaper, timeout), communicator().stringToIdentity("SessionFactory")); + adapter.add(new Glacier2SessionManagerI(logger, reaper), + communicator().stringToIdentity("LibrarySessionManager")); + + // + // Everything ok, let's go. + // + adapter.activate(); + + shutdownOnInterrupt(); + communicator().waitForShutdown(); + defaultInterrupt(); + + reaper.terminate(); + try + { + reaper.join(); + } + catch(InterruptedException e) + { + } + + pool.destroy(); + + return 0; + } +} + +public class Server +{ + static public void + main(String[] args) + { + LibraryServer app = new LibraryServer(); + app.main("demo.Database.library.Server", args, "config.server"); + } +} diff --git a/java/demo/Database/library/Session.ice b/java/demo/Database/library/Session.ice new file mode 100644 index 00000000000..5640227eb05 --- /dev/null +++ b/java/demo/Database/library/Session.ice @@ -0,0 +1,82 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef LIBRARY_SESSION_ICE +#define LIBRARY_SESSION_ICE + +module Demo +{ + +/* Forward declaration. */ +interface Library; + +/** + * + * The session object. This is used to retrieve a per-session library + * on behalf of the client. If the session is not refreshed on a + * periodic basis, it will be automatically destroyed. + * + */ +interface Session +{ + /** + * + * Get the library object. + * + * @return A proxy for the new library. + * + **/ + Library* getLibrary(); + + /** + * + * Refresh a session. If a session is not refreshed on a regular + * basis by the client, it will be automatically destroyed. + * + **/ + idempotent void refresh(); + + /** + * + * Destroy the session. + * + **/ + void destroy(); +}; + +/** + * + * Interface to create new sessions. + * + **/ +interface SessionFactory +{ + /** + * + * Create a session. + * + * @return A proxy to the session. + * + **/ + Session* create(); + + /** + * + * Get the value of the session timeout. Sessions are destroyed + * if they see no activity for this period of time. + * + * @return The timeout (in seconds). + * + **/ + ["nonmutating", "cpp:const"] idempotent long getSessionTimeout(); +}; + +}; + +#endif diff --git a/java/demo/Database/library/SessionFactoryI.java b/java/demo/Database/library/SessionFactoryI.java new file mode 100644 index 00000000000..31ab6716cd3 --- /dev/null +++ b/java/demo/Database/library/SessionFactoryI.java @@ -0,0 +1,46 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class SessionFactoryI extends _SessionFactoryDisp +{ + public synchronized SessionPrx + create(Ice.Current c) + { + SessionI session = new SessionI(_logger, c.adapter); + _SessionTie servant = new _SessionTie(session); + + SessionPrx proxy = SessionPrxHelper.uncheckedCast(c.adapter.addWithUUID(servant)); + + _logger.trace("SessionFactory", "create new session: " + + c.adapter.getCommunicator().identityToString(proxy.ice_getIdentity())); + + _reaper.add(proxy, session); + + return proxy; + } + + public long + getSessionTimeout(Ice.Current c) + { + return _timeout; + } + + SessionFactoryI(Ice.Logger logger, ReapThread reaper, long timeout) + { + _logger = logger; + _reaper = reaper; + _timeout = timeout; + } + + private Ice.Logger _logger; + private ReapThread _reaper; + private long _timeout; +} diff --git a/java/demo/Database/library/SessionI.java b/java/demo/Database/library/SessionI.java new file mode 100644 index 00000000000..6b213e9b0ab --- /dev/null +++ b/java/demo/Database/library/SessionI.java @@ -0,0 +1,96 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import Demo.*; + +class SessionI implements _SessionOperations, _Glacier2SessionOperations +{ + synchronized public LibraryPrx + getLibrary(Ice.Current c) + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + return _library; + } + + synchronized public void + refresh(Ice.Current c) + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + _timestamp = System.currentTimeMillis(); + } + + synchronized public long + getSessionTimeout(Ice.Current c) + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + return 5000; + } + + synchronized public void + destroy(Ice.Current c) + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + + _destroyed = true; + _logger.trace("Session", "session " + c.adapter.getCommunicator().identityToString(c.id) + + " is now destroyed."); + + // This method is never called on shutdown of the server. + _libraryI.destroy(); + c.adapter.remove(_library.ice_getIdentity()); + c.adapter.remove(c.id); + } + + // Called on application shutdown. + synchronized public void + shutdown() + { + if(!_destroyed) + { + _destroyed = true; + _libraryI.shutdown(); + } + } + + synchronized public long + timestamp() + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + return _timestamp; + } + + SessionI(Ice.Logger logger, Ice.ObjectAdapter adapter) + { + _logger = logger; + _timestamp = System.currentTimeMillis(); + _libraryI = new LibraryI(); + _library = LibraryPrxHelper.uncheckedCast(adapter.addWithUUID(new DispatchInterceptorI(_libraryI))); + } + + private Ice.Logger _logger; + private boolean _destroyed = false; // true if destroy() was called, false otherwise. + private long _timestamp; // The last time the session was refreshed. + private LibraryPrx _library; + private LibraryI _libraryI; +} diff --git a/java/demo/Database/library/Token.java b/java/demo/Database/library/Token.java new file mode 100644 index 00000000000..348edff9b58 --- /dev/null +++ b/java/demo/Database/library/Token.java @@ -0,0 +1,40 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +class Token +{ + public static final int TOK_HELP = 0; + public static final int TOK_EXIT = 1; + public static final int TOK_ADD_BOOK = 2; + public static final int TOK_FIND_ISBN = 3; + public static final int TOK_FIND_AUTHORS = 4; + public static final int TOK_FIND_TITLE = 5; + public static final int TOK_NEXT_FOUND_BOOK = 6; + public static final int TOK_PRINT_CURRENT = 7; + public static final int TOK_RENT_BOOK = 8; + public static final int TOK_RETURN_BOOK = 9; + public static final int TOK_REMOVE_CURRENT = 10; + public static final int TOK_STRING = 11; + public static final int TOK_SEMI = 12; + + int type; + String value; + + Token(int t) + { + type = t; + value = null; + } + + Token(int t, String v) + { + type = t; + value = v; + } +} diff --git a/java/demo/Database/library/books b/java/demo/Database/library/books new file mode 100644 index 00000000000..18836dbdba6 --- /dev/null +++ b/java/demo/Database/library/books @@ -0,0 +1,30 @@ +add '096447963X' 'The Dragon Style (Learn to Play Go, Volume III)' 'Janice Kim, Jeong Soo-Hyun' ; +add '0964479613' "Learn to Play Go: A Master's Guide to the Ultimate Game (Volume I)" 'Janice Kim, Jeong Soo-Hyun' ; +add '0964479621' 'The Way of the Moving Horse (Learn to Play Go, Volume II)' 'Janice Kim, Jeong Soo-Hyun' ; +add '0964479648' 'Battle Strategies (Learn to Play Go Series)' 'Janice Kim, Jeong Soo-Hyun' ; +add '0201889544' 'The C++ Programming Language' 'Bjarne Stroustrup' ; +add '0201543303' 'The Design and Evolution of C++' 'Bjarne Stroustrup' ; +add '0201700735' 'The C++ Programming Language Special Edition' 'Bjarne Stroustrup' ; +add '0201379260' 'The C++ Standard Library : A Tutorial and Reference' 'Nicolai M. Josuttis' ; +add '0201749629' 'Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library' 'Scott Meyers' ; +add '0201924889' 'Effective C++: 50 Specific Ways to Improve Your Programs and Design' 'Scott Meyers' ; +add '020163371X' 'More Effective C++: 35 New Ways to Improve Your Programs and Designs' 'Scott Meyers' ; +add '0201615622' 'Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions' 'Herb Sutter' ; +add '020170434X' 'More Exceptional C++' 'Herb Sutter' ; +add '0201704315' 'Modern C++ Design: Generic Programming and Design Patterns Applied' 'Andrei Alexandrescu' ; +add '0735616353' 'Microsoft Visual C++ .NET Deluxe Learning Edition' 'Microsoft Corporation' ; +add '0735615497' 'Programming with Microsoft Visual C++ .NET, Sixth Edition (Core Reference)' 'George Shepherd, David Kruglinski' ; +add '0735614229' 'Applied Microsoft .NET Framework Programming' 'Jeffrey Richter' ; +add '0201824701' 'C++ Primer' 'Stanley B. Lippman, Josee Lajoie' ; +add '0201485184' 'Essential C++' 'Stanley B. Lippman' ; +add '020170353X' 'Accelerated C++: Practical Programming by Example' 'Andrew Koenig, Barbara E. Moo' ; +add '0201423391' 'Ruminations on C++ : A Decade of Programming Insight and Experience' 'Andrew Koenig, Barbara E. Moo' ; +add '0201179288' 'C Traps and Pitfalls' 'Andrew Koenig' ; +add '0131103628' 'The C Programming Language' 'Brian W. Kernighan, Dennis M. Ritchie' ; +add '020161586X' 'The Practice of Programming' 'Brian W. Kernighan, Rob Pike' ; +add '013937681X' 'UNIX Programming Environment, The' 'Brian W. Kernighan, Rob Pike' ; +add '0201563177' 'Advanced Programming in the UNIX(R) Environment' 'W. Richard Stevens' ; +add '0201633469' 'The Protocols (TCP/IP Illustrated, Volume 1)' 'W. Richard Stevens' ; +add '0201634953' 'TCP for Transactions, HTTP, NNTP, and the UNIX(R) Domain Protocols (TCP/IP Illustrated, Volume 3)' 'W. Richard Stevens' ; +add '013490012X' 'UNIX Network Programming, Volume 1: Networking APIs - Sockets and XTI' 'W. Richard Stevens' ; +add '0130810819' 'UNIX Network Programming: Interprocess Communications' 'W. Richard Stevens' ; diff --git a/java/demo/Database/library/build.xml b/java/demo/Database/library/build.xml new file mode 100644 index 00000000000..915b235a438 --- /dev/null +++ b/java/demo/Database/library/build.xml @@ -0,0 +1,55 @@ +<!-- + ********************************************************************** + + Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. + + This copy of Ice is licensed to you under the terms described in the + ICE_LICENSE file included in this distribution. + + ********************************************************************** +--> + +<project name="demo_Database_library" default="all" basedir="."> + + <!-- set global properties for this build --> + <property name="top.dir" value="../../.."/> + + <!-- import common definitions --> + <import file="${top.dir}/config/common.xml"/> + + <target name="generate" depends="init"> + <!-- Create the output directory for generated code --> + <mkdir dir="${generated.dir}"/> + <slice2java outputdir="${generated.dir}" tie="on"> + <meta value="${java2metadata}"/> + <includepath> + <pathelement path="${slice.dir}" /> + </includepath> + <fileset dir="." includes="Library.ice"/> + <fileset dir="." includes="Session.ice"/> + <fileset dir="." includes="Glacier2Session.ice"/> + </slice2java> + </target> + + <target name="compile" depends="generate"> + <mkdir dir="${class.dir}"/> + <javac srcdir="${generated.dir}" destdir="${class.dir}" + debug="${debug}"> + <classpath refid="ice.classpath"/> + <compilerarg value="${javac.lint}"/> + </javac> + <javac srcdir="." destdir="${class.dir}" + excludes="generated/**" debug="${debug}"> + <classpath refid="ice.classpath"/> + <compilerarg value="${javac.lint}"/> + </javac> + </target> + + <target name="all" depends="compile"/> + + <target name="clean"> + <delete dir="${generated.dir}"/> + <delete dir="${class.dir}"/> + </target> + +</project> diff --git a/java/demo/Database/library/config.client b/java/demo/Database/library/config.client new file mode 100644 index 00000000000..9f0fa65a663 --- /dev/null +++ b/java/demo/Database/library/config.client @@ -0,0 +1,62 @@ +# +# The client reads this property to create the reference to the +# "SessionFactory" object in the server. +# +SessionFactory.Proxy=SessionFactory:default -p 10000 + +# +# The proxy to the Glacier2 router for all outgoing connections. This +# must match the value of Glacier2.Client.Endpoints in config.glacier2. +# +#Ice.Default.Router=DemoGlacier2/router:ssl -p 4064 -h 127.0.0.1 + +# +# No active connection management is permitted because of the session +# interfaces. Connections must remain established. +# +Ice.ACM.Client=0 + +# +# Connection retry is not possible because of the session +# interfaces. Connections must remain established. +# +Ice.RetryIntervals=-1 + +# +# Warn about connection exceptions +# +Ice.Warn.Connections=1 + +# +# Network Tracing +# +# 0 = no network tracing +# 1 = trace connection establishment and closure +# 2 = like 1, but more detailed +# 3 = like 2, but also trace data transfer +# +#Ice.Trace.Network=1 + +# +# Protocol Tracing +# +# 0 = no protocol tracing +# 1 = trace protocol messages +# +#Ice.Trace.Protocol=1 + +# +# Security Tracing +# +# 0 = no security tracing +# 1 = trace messages +# +#IceSSL.Trace.Security=1 + +# +# SSL Configuration +# +Ice.Plugin.IceSSL=IceSSL.PluginFactory +IceSSL.DefaultDir=../../../../certs +IceSSL.Truststore=certs.jks +IceSSL.TrustOnly.Client=CN=Server diff --git a/java/demo/Database/library/config.glacier2 b/java/demo/Database/library/config.glacier2 new file mode 100644 index 00000000000..053dbff990b --- /dev/null +++ b/java/demo/Database/library/config.glacier2 @@ -0,0 +1,55 @@ +# +# Set the Glacier2 instance name. +# +Glacier2.InstanceName=DemoGlacier2 + +# +# The client-visible endpoint of Glacier2. This should be an endpoint +# visible from the public Internet, and it should be secure. +# +Glacier2.Client.Endpoints=ssl -p 4064 -h 127.0.0.1 + +# +# The server-visible endpoint of Glacier2. This endpoint is only +# required if callbacks are needed (leave empty otherwise). This +# should be an endpoint on an internal network (like 192.168.x.x), or +# on the loopback, so that the server is not directly accessible from +# the Internet. +# +Glacier2.Server.Endpoints=tcp -h 127.0.0.1 + +# +# The proxy of the session manager. +# +Glacier2.SessionManager=LibrarySessionManager:tcp -h 127.0.0.1 -p 10000 + +# +# For this demo, we use the null permissions verifier. This permissions +# verifier allows any user-id / password combination. +# +Glacier2.PermissionsVerifier=DemoGlacier2/NullPermissionsVerifier + +# +# The timeout for inactive sessions. If any client session is inactive +# for longer than this value, the session expires and is removed. The +# unit is seconds. +# +Glacier2.SessionTimeout=30 + +# +# Security Tracing +# +# 0 = no security tracing +# 1 = trace messages +# +IceSSL.Trace.Security=1 + +# +# SSL Configuration +# +Ice.Plugin.IceSSL=IceSSL:createIceSSL +IceSSL.DefaultDir=../../../../certs +IceSSL.CertAuthFile=cacert.pem +IceSSL.CertFile=s_rsa1024_pub.pem +IceSSL.KeyFile=s_rsa1024_priv.pem +IceSSL.VerifyPeer=0 diff --git a/java/demo/Database/library/config.server b/java/demo/Database/library/config.server new file mode 100644 index 00000000000..ef3eef86199 --- /dev/null +++ b/java/demo/Database/library/config.server @@ -0,0 +1,64 @@ +# +# Configure the server endpoints. +# +SessionFactory.Endpoints=tcp -p 10000:ssl -p 10001 + +# JDBC configuration. +JDBC.Username=USER +JDBC.Password=PASSWORD +JDBC.Url=jdbc:mysql://localhost/library + +# The number of connections in the JDBC connection pool. This number +# should be at least as big as the number of the threads in the server +# thread pool. +JDBC.NumConnections=5 + +# Number of threads in the server-side dispatch thread pool. +Ice.ThreadPool.Server.Size=5 + +# +# How long to keep sessions alive with no activity. Its best to use +# the same value as config.glacier2. +# +SessionTimeout=30 + +# +# Warn about connection exceptions +# +Ice.Warn.Connections=1 + +# +# Network Tracing +# +# 0 = no network tracing +# 1 = trace connection establishment and closure +# 2 = like 1, but more detailed +# 3 = like 2, but also trace data transfer +# +#Ice.Trace.Network=1 + +# +# Protocol Tracing +# +# 0 = no protocol tracing +# 1 = trace protocol messages +# +#Ice.Trace.Protocol=1 + +# +# Security Tracing +# +# 0 = no security tracing +# 1 = trace messages +# +#IceSSL.Trace.Security=1 + +# +# SSL Configuration +# +Ice.Plugin.IceSSL=IceSSL.PluginFactory +IceSSL.VerifyPeer=0 +IceSSL.DefaultDir=../../../../certs +IceSSL.Keystore=server.jks +IceSSL.Password=password +IceSSL.Truststore=certs.jks diff --git a/java/demo/Database/library/createTypes.sql b/java/demo/Database/library/createTypes.sql new file mode 100644 index 00000000000..ec5fc72e5c3 --- /dev/null +++ b/java/demo/Database/library/createTypes.sql @@ -0,0 +1,43 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2008 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** +# +# Initialize SQL tables. +# +DROP TABLE IF EXISTS books; +CREATE TABLE books +( + id INT UNSIGNED AUTO_INCREMENT NOT NULL, + PRIMARY KEY (id), + isbn CHAR(10), + title VARCHAR(255), + renter_id INT +) ENGINE=InnoDB; + +DROP TABLE IF EXISTS authors_books; +CREATE TABLE authors_books +( + book_id INT, + author_id INT +) ENGINE=InnoDB; + +DROP TABLE IF EXISTS authors; +CREATE TABLE authors +( + id INT UNSIGNED NOT NULL AUTO_INCREMENT, + PRIMARY KEY(id), + name VARCHAR(255) +) ENGINE=InnoDB; + +DROP TABLE IF EXISTS customers; +CREATE TABLE customers +( + id INT UNSIGNED NOT NULL AUTO_INCREMENT, + PRIMARY KEY(id), + name VARCHAR(255) +) ENGINE=InnoDB; |