Beautiful Code [247]
During development, we used the open source Apache Log4J Java package to log nearly everything that occurred in the middleware services. It was certainly useful for debugging during development. Logging enabled us to write code that was more reliable. Whenever there was a bug, the logs told us what was going on just prior to the problem, and so we were better able to fix the bug.
We originally intended to reduce the logging only to serious messages before CIP went into operation. But we ended up leaving most of the logging on since it had a negligible impact on overall performance. Then we discovered that the logs gave us much useful information not only about what was going on with each service, but also how client applications were using the services. By analyzing the logs (which we called "log mining"), we were able to tune the services for better performance based on empirical data (see "Dynamic Reconfiguration," later in this chapter).
Here are some code snippets from the streamer service provider bean that show how we did logging for file downloading. The getDataFile( ) method processes each "Get Data File" request (via web services) from the client applications. The method immediately logs the request (lines 15–17), including the user ID of the requester and the filepath of the desired source file:
Code View: Scroll / Show All
1 public class StreamerServiceBean implements SessionBean
2 {
3 static {
4 Globals.loadResources("Streamer");
5 };
6
7 private static Hashtable readerTable = new Hashtable( );
8 private static Hashtable writerTable = new Hashtable( );
9
10 private static BeanCacheStats cacheStats = Globals.queryStats;
11
12 public FileToken getDataFile(AccessToken accessToken, String filePath)
13 throws MiddlewareException
14 {
15 Globals.streamerLogger.info(accessToken.userId( ) +
16 ": Streamer.getDataFile("
17 + filePath + ")");
18 long startTime = System.currentTimeMillis( );
19
20 UserSessionObject.validateToken(accessToken);
21 FileToken fileToken = doFileDownload(accessToken, filePath);
22 cacheStats.incrementTotalServerResponseTime(startTime);
23 return fileToken;
24 }
25
The doFileDownload( ) method creates a new file token (line 30) and file reader bean (line 41), and then calls the reader bean's getDataFile( ) method (line 42). The cacheStats field deals with runtime monitoring, which is described later:
Code View: Scroll / Show All
26 private static FileToken doFileDownload(AccessToken accessToken,
27 String filePath)
28 throws MiddlewareException
29 {
30 FileToken fileToken = new FileToken(accessToken, filePath);
31 String key = fileToken.getKey( );
32
33 FileReaderLocal reader = null;
34 synchronized (readerTable) {
35 reader = (FileReaderLocal) readerTable.get(key);
36 }
37
38 // Create a file reader bean to start the download.
39 if (reader == null) {
40 try {
41 reader = registerNewReader(key);
42 reader.getDataFile(filePath);
43
44 return fileToken;
45 }
46 catch(Exception ex) {
47 Globals.streamerLogger.warn("Streamer.doFileDownload("
48 + filePath + "): " +
49 ex.getMessage( ));
50 cacheStats.incrementFileErrorCount( );
51 removeReader(key, reader);
52 throw new MiddlewareException(ex);
53 }
54 }
55 else {
56 throw new MiddlewareException("File already being downloaded: " +
57 filePath);
58 }
59 }
60
The readDataBlock( ) method processes each "Read Data Block" request from the client applications. It looks up the correct file reader bean (line 71) and calls the reader bean's readDataBlock( ) method (line 79). At the end of the source file, it removes the file reader bean (line 91):
Code View: Scroll / Show All
61 public DataBlock readDataBlock(AccessToken accessToken, FileToken fileToken)
62 throws MiddlewareException
63 {
64 long startTime = System.currentTimeMillis( );
65 UserSessionObject.validateToken(accessToken);
66
67 String key = fileToken.getKey( );
68
69 FileReaderLocal reader = null;
70 synchronized (readerTable) {
71 reader = (FileReaderLocal) readerTable.get(key);
72 }
73
74 // Use