There was a problem previewing this document. Retrying... Download. Connect more apps... Try one of the apps below to op
Server-Based Java Programming
Server-Based Java Programming
TED NEWARD
MANNING Greenwich (74° w. long.)
For online information and ordering of this and other Manning books, go to www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact: Special Sales Department Manning Publications Co. 32 Lafayette Place Greenwich, CT 06830
Fax: (203) 661-9018 email:
[email protected]
©2000 by Manning Publications Co. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps. Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end.
Manning Publications Co. 32 Lafayette Place Greenwich, CT 06830
Copyeditor: Elizabeth R. Martin Typesetter: Denis Dalinnik Cover designer: Leslie Haimes
Printed in the United States of America 1 2 3 4 5 6 7 8 9 10 – CM – 03 02 01 00
“To you, the reader—yes, you. A book without a reader is a pretty pointless exercise. Thank you.”
brief contents 1
✧
Enterprise Java
1
2
✧
ClassLoaders
3
✧
Custom ClassLoaders
4
✧
Extensions
5
✧
Threads
6
✧
Threading issues
7
✧
Control
8
✧
Remote control
9
✧
Configuration
10
✧
Sockets
237
11
✧
Servlets
283
12
✧
Persistence
13
✧
Business objects
14
✧
Business object models
15
✧
Middleware
16
✧
Java Native Interface
17
✧
Monitoring
27 61
93 126 149
174 208 225
300 340 372
402 510
vii
463
contents foreword xv preface xvii acknowledgments xxi about this book xxiii goals of this book xxix about the author xxxi about the cover illustration xxxiii
1 Enterprise Java 1.1
1
Enterprise development 1 What is enterprise development? 1 G Developing the enterprise application 4 G Reinventing the wheel 7
1.2
Three zeroes 8 Zero development 9
1.3
Sun’s view 14
1.4
G
Zero deployment 11
G
Zero administration 12
Java in the enterprise 14 G
Alternate views 14
Why Java? 15 Criticisms of Java as a server-side language 17
1.5 1.6
Summary 25 Additional reading 26
2 ClassLoaders 2.1
27
Dynamic linking 28 Run-time dynamic loading 28
2.2
G
Reflection 31
ClassLoaders: rules and expectations 37 Java .class file format 37 G Using ClassLoader 39 java.lang.ClassLoader 41 G Java name spaces 48
ix
2.3
Java’s built-in ClassLoaders 49 java.security.SecureClassLoader 49 G java.net.URLClassLoader 49 sun.applet.AppletClassLoader 57 G java.rmi.server.RMIClassLoader 57 Bootstrap ClassLoader 57 G sun.misc.Launcher$ExtClassLoader 58
2.4 2.5
Summary 58 Additional reading 59
3 Custom ClassLoaders 3.1
61
Extending ClassLoader 61 FileSystemClassLoader 62 G HashtableClassLoader 66 CompilerClassLoader 67 G StrategyClassLoader and ClassLoaderStrategy 71 G CompositeClassLoader 75 Other ClassLoader tricks 79 G Other ClassLoaders 80
3.2 3.3
On-the-fly code upgrades 80 GJAS: first steps 85 Goals 85
3.4
Service 86
G
Server 88
G
ServerManager 90
Summary 92
4 Extensions 4.1
G
93
Types of extensions 94 Installed extensions 94 G Building an installed extension 95 Download extensions 96 G Building a download extension 98
4.2
Implications of the extensions mechanism 100 Distributed libraries through download extensions 100 Java EXEs; relation to C++ static linking 101
4.3
Packaging extensions 102 The build-time vs. run-time dilemma 103
4.4
The plug-in 104 The plug-in concept 105 G Enter plug-ins 107 G Marking a .jar file as a plug-in 110 G PluginClassLoader 111 G Example: PluginApp 118 G Uses for plug-ins 124
4.5
Summary 125
5 Threads 5.1
126
Why threads? 127 Concurrent processing 127 G Scalability per machine 128 Encapsulation 129 G Design and implementation 130
5.2
Java threads 130 java.lang.Thread and java.lang.Runnable 131 G Starting threads 137 Stopping threads 139 G Daemon threads 142 G Threads and ClassLoaders 143 G java.lang.ThreadGroup 144
x
CONTENTS
5.3
Thread implementations in Java 146 Green threads 147 Implications 148
5.4 5.5
G
Hybrids 147
Summary 148 Additional reading 148
6 Threading issues 6.1
Native threads 147
G
149
Synchronization 150 Thread-local storage 152
6.2 6.3
Exception-handling with multiple threads 153 Thread idioms and patterns 158 Client-Dispatcher-Server 158 G Fire-and-forget 159 Active Object 160 G SpinLoop 160 Polling (PeriodicThread) 161 G DelayedFire (ScheduledThread) 163 G Futures 164
6.4
GJAS 166 Adding thread support to GJAS 167
6.5 6.6
Summary 173 Additional reading 173
7 Control 7.1
174
GJAS 175 Local implementation 175
7.2 7.3 7.4
ThreadServer 196
8 Remote control 8.1
G
Example: HelloService 186
Testing the LocalServer implementation 187 ExecService 189 HelloAgainService 193 G
Example: ConsoleControlService 201
208
RMI implementation 209 Analysis 217
8.2 8.3 8.4
Other implementations 218 Necessary improvements 219 Additional reading 224
9 Configuration 9.1
225
Java models 225 Interface: ConfigProperty and ConfigProperties 226 Configuration front ends 235
9.2 CONTENTS
G
Usage 233
Summary 236
xi
10 Sockets
237
10.1 Simple socket services 237 SocketClient 238 Analysis 246
G
EchoService 243
G
TimeService 245
10.2 Encapsulation and refactoring 247 SocketServer 247
G
Example: Echo2Service 254
10.3 Connection and ConnectionManager 255 Example: EchoConnection 262 Servlets 272
G
Example: HTTPConnection 263
10.4 Advanced Socket services 273 SocketClassLoader and SocketClassService 273 Concept: RedirectorService 279 G Concept: FilterService 280 Other types 281
10.5 Summary 281 10.6 Additional reading 282
11 Servlets
283
11.1 Relationship to sockets 283 CodeServlet: A filtering servlet 285 G HeaderFooter: a redirecting servlet 287 G Server-side scripting capabilities 289 G Servlets: Not just about HTML anymore 290
11.2 Servlets and the n-tier application 292 Separating logic from content 293
11.3 Servlets as a poor man’s RMI 293 Example: RemoteStorageServlet 295 Concept: SOAP 298
G
Concept: poor man’s RMI 297
11.4 Summary 298 11.5 Additional reading 298
12 Persistence
300
12.1 Java Serialization 301 Serialization to other places 302 G Security and Serialization 303 Customized Serialization 306 G Serialization and evolution 309 Replacement 313
12.2 Beyond the specification 317 Remote storage of objects 317 G Example: RemoteStorageService and RemoteStorageClient 318 G Remote construction of objects 323 Example: RemoteObjectFactory 325
xii
CONTENTS
12.3 JDBC 330 Transient ; log("Exiting ServerManager.getServices(); list = " + list); return svrArray; }
The getServices method, on the other hand, requires a bit more work. It uses an Enumeration returned from m_servers to build an array of Strings to be returned to the caller. Note that it also echoes this list of Services to the log, providing a convenient debugging aid. The array of Strings is then returned. /** * Obtain a reference to a Server instance by ID. If it can’t be found * (perhaps it’s shut down since the user obtained the ID?), then return * a null instance. */ public IServer getService(String instanceID) {
180
CHAPTER 7
CONTROL
return (IServer)m_servers.get(instanceID); }
The getService method, given what we saw in addService, is about as simple as they come—it takes the passed-in String, and asks the dictionary of IServers for the IServer instance answering to that title. The dictionary either returns null, indicating it’s never heard of the IServer by that name, or it returns the IServer instance. public void log(String msg) { if (m_log != null) { StringBuffer m = new StringBuffer(); m.append(new Date()); m.append(" [" ); m.append(Thread.currentThread().toString()); m.append("]: "); m.append(msg); m_log.println(m); System.out.println(m); m_log.flush(); } } public void log(Exception ex) { if (m_log != null) { log("Exception raised: " + ex.toString()); PrintWriter pw = new PrintWriter(getLogStream()); pw.println(new Date() + " Exception raised: " + ex.toString()); ex.printStackTrace(pw); pw.flush(); } } public void error(String msg) { if (m_err != null) { StringBuffer m = new StringBuffer(); m.append(new Date()); m.append(" [" ); m.append(Thread.currentThread().toString()); m.append("]: *** ERROR *** "); m.append(msg); m_err.println(m); m_err.flush(); } } public void error(Exception ex)
GJAS
181
{ if (m_err != null) { error(": Exception raised: " + ex.toString()); PrintWriter pw = new PrintWriter(getErrStream()); pw.println(new Date() + " Exception raised: " + ex.toString()); ex.printStackTrace(pw); pw.flush(); } } //=========================================== // LocalServerManager-specific methods // /** * Return the OutputStream used for writing to the log. */ public OutputStream getLogStream() { return m_logStream; } /** * Set the OutputStream used for writing to the log. */ public void setLogStream(OutputStream os) { m_logStream = os; if (m_logStream != null) m_log = new PrintWriter(m_logStream); else m_log = null; } /** * Return the OutputStream used for writing errors. */ public OutputStream getErrStream() { return m_errStream; } /** * Set the OutputStream used for writing errors. On your head * be the consequences if you set this to null! */ public void setErrStream(OutputStream os) { m_errStream = os; if (m_errStream != null) m_err = new PrintWriter(m_errStream); else m_err = null; }
182
CHAPTER 7
CONTROL
The log and error methods write String and Exception objects to their respective OutputStreams. LocalServerManager also provides getLogStream, setLogStream, getErrorStream, and setErrorStream methods to get and set the log and error OutputStream objects, so that users within the JVM in which the LocalServerManager is running can redirect output where desired. // main not shown here; see LocalServerManager.java for details
Finally, LocalServerManager provides a main method as a means of using LocalServerManager directly from the command line; however, we’ll see later other (more effective and/or efficient) ways of kicking off the GJAS backplane. // Internal data // private Dictionary m_servers = new Hashtable(); private private private private
OutputStream m_logStream = null; OutputStream m_errStream = System.err; PrintWriter m_log = null; PrintWriter m_err = new PrintWriter(m_errStream);
}
There’s nothing really earth-shattering about LocalServerManager.java; note that, as pointed out in the javadoc class comment block, this implementation uses the normal system ClassLoader to load all Services, so that the dynamic upgrade on-the-fly approach isn’t possible, since we can’t unload the system ClassLoader. We’ll see how to make use of that later in this chapter. To go along with the LocalServerManager, listing 7.1 shows LocalServer class, some of which we talked about in chapter 4: Listing 7.1 Code for LocalServer /** * Server wraps the Service instance, using Future calls to help preserve * the responsiveness and robustness of the ServerManager. */ public class LocalServer implements IServer { // Prevent no-arg object instantiation // private LocalServer() {} /** * Construct a Server around a Service instance. */ public LocalServer(Service svc) { m_service = svc; }
GJAS
183
/** * Start the wrapped Service instance. Services have 15 seconds in * which to either initialize, or else start a thread to perform the * necessary initialization and return. If a Service fails to respond * within 15 seconds of the start of its start call, the Server and/or * ServerManager are free to destroy it. */ public boolean start(final String[] args) { // We want to fire off a Thread to make the start() call, and wait // up to 15 seconds to see if we return. If we don’t by the time // the 15 seconds are up, we assume the Service has run off into // Limbo and needs to be killed. (Most Services of any complexity // will need to fire off their own Thread to do their work, so // their start() methods should come back pretty quickly.) // try { FutureResult futureResult = new FutureResult(); Runnable cmd = futureResult.setter(new Callable() { public Object call() { try { m_service.start(args); ServerManager.instance().log( m_service.getClass().getName() + ": started"); } catch (Exception ex) { m_exception = ex; ServerManager.instance().log(ex); } return null; } }); new ThreadedExecutor().execute(cmd); futureResult.timedGet(15*1000); // we want to wait 15 seconds, no more. return true; } catch (TimeoutException tEx) { m_exception = tEx; // The Service ran out of time starting up; kill it, note the // failure to start, and return // ServerManager.instance().log(tEx); } catch (InterruptedException iEx)
184
CHAPTER 7
CONTROL
{ m_exception = iEx; // For some reason, the thread doing the call failed; note the // failure to start, and return // ServerManager.instance().log(iEx); } catch (InvocationTargetException itEx) { m_exception = itEx; // Java Reflection failed; note the failure, and return // ServerManager.instance().log(itEx); } catch (Exception ex) { m_exception = ex; ServerManager.instance().log(ex); } return false; } // stop(), pause(), resume(), getState() and getInstanceID() // all are simple variations on start(), above, and are not // shown here public void kill() { m_service = null; System.gc(); } public Exception getLastError() { return m_exception; } // Internal data // private Service m_service = null; private Exception m_exception = null; }
If you look at the LocalServer.java code in the com.javageeks.gjas package, you’ll notice that most of the length deals with using Threads (via Lea’s FutureResult class from the Concurrent class library) to isolate the calls into the Service without blocking the entire system should the call hang or disappear. Everything else is either straightforward, or scaffolding to support the Service operations. At this point, we’ve presented the basic skeleton for a running GJAS system, with one notable exception: we have no Services with which to test it! GJAS
185
7.1.2
Example: HelloService We start with the GJAS-equivalent of the canonical first program written for any new system. HelloService simply writes “Hello, world!” to the console when it is started (listing 7.2). Listing 7.2 Code for HelloService package com.javageeks.gjas.services.sample; import com.javageeks.gjas.*; public class HelloService implements Service { public HelloService() { } public void start(String[] args) throws Exception { // We’re starting // m_state = STARTING; // Print out “Hello, world!” // System.out.println(“Hello, world! –-From, HelloService”); // We write the contents of args to the console, one line // per element in the array // for (int i=0; i