CSE 503 - Homework 4 Problem 1 - Information Hiding Charlie Reis, 6-2-2004 1A) Violating Information Hiding i) Class com.neoworks.jukex.sqlimpl.JukeXTrackStore JukeXTrackStore violates information hiding by returning private mutable data objects from public methods. Specifically, on line 481 of method getTrackIds(), the class returns a reference to its private static long _trackIds[] array. This allows external callers to manipulate the contents of the array without JukeXTrackStore's knowledge, potentially breaking invariants. Future changes to JukeXTrackStore, such as changing the underlying data representation or even changing how it was used within the existing invariants, could break clients that manipulated this array in the past. The relevant code snippets are reproduced below. private static long[] _trackIds = null; // ... public long[] getTrackIds() { Connection conn = null; if (_trackIds == null || _trackIds.length != getTrackCount()) { _trackIds = new long[(int)getTrackCount()]; } else { return _trackIds; } // ... [Note: this bug was found by the FindBugs tool.] ii) Inner class com.neoworks.jukex.sqlimpl.JukeXTrackStore$JukeXTrackLoader JukeXTrackLoader violates information hiding by making assumptions about the type of objects returned by other classes. Specifically, on line 730 of the public Map getTracks() method, it casts the return value of TrackStoreFactory's method getTrackStore() from TrackStore to JukeXTrackStore: public Map getTracks() { // ... JukeXTrackStore trackStore = (JukeXTrackStore) TrackStoreFactory.getTrackStore(); // ... Such a cast prevents TrackStoreFactory from returning an alternate representation in the future. If JukeXTrackLoader relies on this assumption, it is unclear why it did not simply call JukeXTrackStore's getInstance() method, or indeed (since this cast is performed in a non-static method in a non-static inner class) use "JukeXTrackStore.this" in the first place. On line 741 of the same method, JukeXTrackLoader also casts the return type of JukeXTrackStore.getCachedTrack() from Track to JukeXTrack, preventing JukeXTrackStore from using different implementations of Track (possibly in different contexts). while ( rs.next() ) { lastID = currID; currID = new Long( rs.getLong( 1 ) ); // Check if we've changed track if (!lastID.equals(currID)) { currTrack = (JukeXTrack) trackStore.getCachedTrack( currID.longValue() ); // ... [Note: these bugs were found using the FindBugs tool and Vibha's "Assumptions" Eclipse plug-in.] 1B) Respecting Information Hiding Class com.neoworks.jukex.connectionpool.ConnectionPool does a good job respecting the principle of information hiding, concealing both its internal representations and package-specific data types. Most notably, its use of the ConnectionWrapper class is never exposed to any class outside the package, or even to PoolManager. For example, on line 117, the getConnection() method conceals the use of ConnectionWrapper by returning type Connection: /** * Get a connection from the pool. * * @return The next available Connection * @exception SQLException If the request for a connection times out or fails. */ public Connection getConnection() throws SQLException { //Log.report("Request for connection received", Log.DEBUG); try { Connection conn = getConnection(timeOut * 1000); return new ConnectionWrapper(conn, this); } catch (SQLException e) { log.error("Encountered an exception getting a database connection", e); throw e; } } ConnectionPool also has several private fields that it does not expose to other classes, including a freeConnections Vector which could have been manipulated if it was ever returned as a value. The class itself is also well hidden by PoolManager, as no classes outside the com.neoworks.jukex.connectionpool package make reference to ConnectionPool. In this respect, information hiding could have been better enforced by making this class package private rather than public. [Note: analysis was aided by Vibha's "DeFacto Interfaces" Eclipse plug-in, as well as grep.] 1C) Cohesion and Coupling i) JukeXTrackStore JukeXTrackStore exhibits a form of functional cohesion along one axis but not another. Together with the other classes in its package, it encapsulates all interactions with the MySQL database. In this sense, its methods perform similar activities as viewed externally, in that they all interact with the database. At the same time, JukeXTrackStore is not functionally cohesive in terms of the elements of the program that it handles. Specifically, its name implies that it handles storage of tracks, and external callers could reasonably expect attributes and playlists to be functionally different concerns. However, since tracks, attributes, and playlists are all stored in the database, they all are handled by JukeXTrackStore. A more cohesive solution would be to provide a class for interacting with the database, and separate classes for handling tracks, attributes, and playlists, each of which might be stored in the database or might be stored in another form in the future without affecting the database classes. In terms of coupling, JukeXTrackStore is used by many different classes in many packages, and it builds upon many classes in many packages, but these sets are essentially disjoint. This indicates that JukeXTrackStore builds upon lower-level modules to provide a useful abstraction to higher-level modules, exhibiting a low degree of coupling between modules, since there are very few bidirectional dependencies. The exceptions to this are described below. Vibha's Information Hiding Eclipse plug-in reveals the classes that JukeXTrackStore depends on, including Java library classes, Log4J and SQL classes, and the com.neoworks.util package. It also uses com.neoworks.connectionpool.PoolManager, several classes in com.neoworks.jukex (ie. TrackStoreFactory, TrackStore, Track, and DatabaseObject), and a few classes in its own package (JukeXTrack, JukeXAttribute, and JukeXPlaylist). Of these, the only class outside com.neoworks.jukex.sqlimpl that is aware of JukeXTrackStore is TrackStoreFactory, which returns the singleton instance of JukeXTrackStore for many different clients. This maintains low coupling, allowing most other packages to be unaware of the sqlimpl package. The main exception is the frequent use of JukeXTrackStore's DB_NAME constant, which increases coupling unnecessarily. (This constant could have been hidden in the sqlimpl package without having the rest of the clients know about it.) It is also worth noting that com.neoworks.jukex.sqlimpl.BatchTrackLoader is used by the com.neoworks.jukex.TrackStore interface, and likely belongs in the com.neoworks.jukex package (further reducing the dependence of that package on JukeXTrackStore's package). Many classes in different packages do use JukeXTrackStore, but JukeXTrackStore itself is unaware of these uses, indicating low coupling. These packages include com.neoworks.jukex.tracksource, com.neoworks.jukex.client.tui, com.neoworks.jukex.client.html.standard, com.neoworks.jukex.query, com.neoworks.shout, and the default package (as revealed by Eclipse's call hierarchy for TrackStore's getTrackStore method). Classes in these same packages use the DB_NAME constant, which (as mentioned above) increases coupling unnecessarily. ii) JukeXTrackLoader JukeXTrackLoader is designed for a single purpose, indicating high internal cohesion. It solely exists to provide batch track loading functionality for JukeXTrackStore, and consists mainly of logic to load tracks using a SQL query in its getTracks() method on line 687. It provides no other functionality of its own, and it relies on other classes to perform general operations to accomplish its task. Interestingly, according to Eclipse's call hierarchy, JukeXTrackLoader is never used. It is only created within the getBatchTrackLoader() method in JukeXTrackStore (on line 632), but this method is never called. Like JukeXTrackStore, JukeXTrackLoader builds upon classes in a few basic modules without their knowledge. Besides classes in its own package (namely JukeXTrackStore, JukeXTrack, and JukeXAttributeValue), it uses several Java and SQL library classes, as well as PoolManager, com.neoworks.jukex.TrackStoreFactory, and classes in the com.neoworks.util package. Each of these has no knowledge of this class and (as described above) minimal knowledge of its entire package. Thus, JukeXTrackLoader has low coupling, merely building upon other modules to provide an abstraction. iii) ConnectionPool The com.neoworks.jukex.connectionpool package exhibits good cohesion, though all three of its classes (ConnectionPool, ConnectionWrapper, and PoolManager) are concerned with the same design decision. As a whole, these few classes provide a good abstraction to the rest of the system for obtaining a database connection, allowing other modules to simply request connections without caring that they are pooled for improved performance. The ConnectionPool class also has a low degree of external coupling; in fact, no classes outside its package ever reference it directly. Even within the package, ConnectionWrapper only uses ConnectionPool's wrapperClosed method (line 80), and PoolManager only uses ConnectionPools' constructor, release, and getConnection methods (lines 308, 411; 432; 460 respectively). [Note: analysis was aided by Vibha's "DeFacto Interfaces" Eclipse plug-in, the "other classes used" feature of Vibha's "Information Hiding" Eclipse plug-in, and Eclipse's own call hierarchy feature.] CSE 503 - Homework 4 Problem 2 - Logging Concern Charlie Reis, 6-2-2004 2A) Logging Concern in com.neoworks.jukex.sqlimpl package All occurrences of this concern are identified below, broken down by class, method name, and line number. ------------------------------------------------------------ ----------[In file BatchTrackLoader.java]---------- ----------[In file InMemoryPlaylist.java]---------- ----------[In file JukeXAttribute.java]---------- ----------[In file JukeXAttributeValue.java]---------- 23-import com.neoworks.jukex.*; 24- 25:import org.apache.log4j.Category; 26- -- [Field declarations] 34-public class JukeXAttributeValue implements AttributeValue, DatabaseObject 35-{ 36: private static final Category log = Category.getInstance(JukeXAttributeValue.class.getName()); 37: private static final boolean logDebugEnabled = log.isDebugEnabled(); 38: private static final boolean logInfoEnabled = log.isInfoEnabled(); 39- -- [Constructor] 105- if ( !newEntryId.next() ) 106- { 107: log.error("Something awful happened. An INSERT somehow failed to appear in the database"); 108- } -- 115- catch ( Exception e ) 116- { 117: log.error( "Encountered an exception whilst trying to create a String AttributeValue" , e ); 118- } -- [setString] 182- catch ( Exception e ) 183- { 184: log.error("Encountered an exception attempting to change an AttributeValue string value"); 185- } ----------[In file JukeXPlaylist.java]---------- 38-import com.neoworks.util.StringDecorator; 39- 40:import org.apache.log4j.Category; 41- -- [Field declarations] 51-public class JukeXPlaylist extends TrackSourcePipelineElementSkeleton implements Playlist 52-{ 53: private static final Category log = Category.getInstance(JukeXPlaylist.class.getName()); 54: private static final boolean logDebugEnabled = log.isDebugEnabled(); 55: private static final boolean logInfoEnabled = log.isInfoEnabled(); 56- -- [getNextTrack] 128- retVal = getTrack(0); 129- } else { 130: if (logDebugEnabled) log.debug("I'm spent, delegating..."); 131- retVal = delegateGetNextTrack(); -- [peekTracks] 156- { 157- int rem = count - ll.size(); 158: if (logDebugEnabled) log.debug("Peeking ahead for " + count + "tracks, remainder " + rem); 159- List retVal = new ArrayList(); -- [readTrackListing] 200- catch ( SQLException se ) 201- { 202: log.error( "Failed due to an exception reading a Track listing into a playlist" , se ); 203- } catch (Exception e) { 204: log.warn("Encountered exception while reading track listing: ", e); 205- } -- [persist] 251- { 252- try { conn.rollback(); } catch ( SQLException ignore ) { } 253: log.error( "Encountered an error persisting a playlist" , se ); 254- } ----------[In file JukeXTrack.java]---------- 27-import com.neoworks.connectionpool.PoolManager; 28- 29:import org.apache.log4j.Category; 30- -- [Field declarations] 44-public class JukeXTrack implements Track, DatabaseObject 45-{ 46: private static final Category log = Category.getInstance(JukeXTrack.class.getName()); 47: private static final boolean logDebugEnabled = log.isDebugEnabled(); 48: private static final boolean logInfoEnabled = log.isInfoEnabled(); 49- -- [addAttributeValue] 119- else 120- { 121: log.error( "JukeXTrack encountered an attribute with an unknown type ["+attribute.getType()+"]" ); 122- } -- 128- catch ( Exception e ) 129- { 130: log.error( "JukeXTrack encountered an exception whilst attempting to add an AttributeValue" , e ); 131- } -- [clearAttribute] 152- catch ( Exception e ) 153- { 154: log.error("Exception encountered attempting to clear attribute values",e); 155- } -- [readAttributesFromDB] 198- currAttributeStringValue = rs.getString( 4 ); 199- 200: //if (logDebugEnabled) log.debug ("Name: "+ currAttributeName+" Type: "+currAttributeType+" StringVal: "+currAttributeStringValue+" NumVal: "+currAttributeNumericValue); 201- -- 210- catch (Exception e) 211- { 212: log.error( "Encountered an exception whilst reading attributes from the database" , e ); 213- } -- [setUpdatedDate] 252- public void setUpdatedDate( java.util.Date newdate ) 253- { 254: log.error( "Updating track "+_id+" date to: " + newdate + " ["+newdate.getTime()+"]" ); 255- Connection conn = null; -- 267- catch ( SQLException se ) 268- { 269: log.error( "Exception whilst changing modified date on track with id="+this._id , se ); 270- } -- [getAttributeValue(Attribute)] 298- } 299- } 300: log.warn("No values for attribute "+attribute.getName()); 301- return null; -- [getAttributeValue(String)] 315- return getAttributeValue( a ); 316- } 317: log.warn("Cannot find attribute name " + attributename); 318- return null; ----------[In file JukeXTrackStore.java]---------- 41-import com.neoworks.util.MultiMap; 42- 43:import org.apache.log4j.Category; 44- -- [Field declarations] 51-public class JukeXTrackStore implements TrackStore 52-{ 53: private static final Category log = Category.getInstance(JukeXTrackStore.class.getName()); 54: private static final boolean logDebugEnabled = log.isDebugEnabled(); 55: private static final boolean logInfoEnabled = log.isInfoEnabled(); 56- -- [getTrackCount] 128- } 129- } catch ( Exception e ) { 130: log.error( "An exception was encountered whilst trying to count the number of tracks" , e ); 131- } finally { -- [getTrack(URL)] 165- catch ( Exception e ) 166- { 167: log.error( "An exception was encountered whilst trying to retrieve a track with the URL ["+url+"]" , e ); 168- } -- [getTrack(long)] 225- catch ( Exception e ) 226- { 227: log.error( "An exception was encountered whilst trying to retrieve a track with id: "+id , e ); 228- } -- [getTracks] 265- else 266- { 267: log.warn( "Could not retrieve all tracks specified in a getTracks() call. Track "+currID+" could not be found" ); 268- resultList.add( y , null ); -- [storeTrack] 308- if ( !id.next() ) 309- { 310: log.fatal("Something went really badly wrong whilst trying to store a track. Could not fetch the LAST_INSERT_ID()."); 311- } -- 317- catch ( Exception e ) 318- { 319: log.error( "Exception encountered whilst storing track with url ["+url+"]" , e ); 320- } -- [getAttribute] 349- catch (SQLException se) 350- { 351: log.error( "Encountered an SQL error attempting to retrieve an attribute" , se ); 352- } -- [getAttributes] 386- catch ( SQLException se ) 387- { 388: log.error( "Encountered an exception whilst fetching attributes from the database" , se ); 389- } -- [createAttribute] 424- if ( getAttribute( name ) != null ) 425- { 426: log.error( "Skipping duplicate addition of attribute ["+name+"]" ); 427- return getAttribute( name ); -- 450- catch ( SQLException se ) 451- { 452: log.error( "Encountered an exception whilst creating an attribute in the database" , se ); 453- } -- [getTrackIds] 498- catch ( SQLException se ) 499- { 500: log.error( "Encountered an exception whilst getting track ids from the database" , se ); 501- } -- [getPlaylist] 537- catch ( SQLException se ) 538- { 539: log.error( "Encountered an exception whilst getting a playlist from the database" , se ); 540- } -- [createPlaylist] 581- catch ( SQLException se ) 582- { 583: log.error( "Failed due to an Exception whilst creating a playlist" , se ); 584- } -- [loadPlaylists] 600- private void loadPlaylists() 601- { 602: if (logDebugEnabled) log.debug( "Loading playlists from database..." ); 603- -- 617- catch ( SQLException se ) 618- { 619: log.error( "Encountered an exception whilst reading the playlists from the database" , se ); 620- } -- [JukeXTrackLoader.getTracks] 769- catch ( SQLException se ) 770- { 771: log.warn( "Batch getter encountered an exception whilst retrieving tracks" , se ); 772- } ------------------------------------------------------------ Technique: I read source code to ensure all uses of Log4J's Category object were with a variable named log. This suggested the simple use of grep to find all references to "log", with line numbers and context for each match. I used the script below to find all occurrences for each class, and then manually annotated them with the method names. (Eclipse helped with this by searching for all uses of the "log" variable and drawing a yellow arrow next to each one.) #!/bin/tcsh set files = (*.java) foreach f ($files) echo "----------[In file $f]----------" grep -n -A 1 -B 2 "log" $f echo end 2B) Modularize Concern in Java Yes, it is possible to modularize this logging concern in standard Java, but it comes at great expense in implementation complexity and even some performance overhead. Each of these logging messages is reacting to an event (most often exceptions, but also invariant violations and simple events like setUpdatedDate). It would be possible to decouple the events from the logging, allowing arbitrary actions to be taken when those events occur. Specifically, these classes could use the Observer design pattern, notifying registered listeners when the events occur. A LoggingObserver could then log the appropriate message based on the event type, if logging was enabled. If logging is disabled, the LoggingObserver would not even need to be registered as an observer. Thus, the logging concern would be separated from the (admittedly more complex) code. Such a technique would allow other concerns to observe the same events (eg. more robust exception handling or user notification of errors), but it would require foreknowledge of which events to define. 2C) Modularize Concern in AspectJ It is possible to modularize most of the logging concern using AspectJ, but several log messages occur in extremely specific cases which would be extremely difficult to express in aspects (without an in-depth knowledge of AspectJ). Most of the instances of the concern are covered in the aspect below, but the following methods have logging concerns too specific to easily express in an aspect, since they depend on locally scoped values which AspectJ does not appear to expose via pointcuts. - JukeXAttributeValue's constructor - JukeXPlaylist.peekTracks - JukeXTrack.addAttributeValue - JukeXTrackStore.getTracks(long[]) - JukeXTrackStore.storeTrack - JukeXTrackStore.createAttribute package com.neoworks.jukex.sqlimpl; import org.apache.log4j.Category; import java.sql.SQLException; import com.neoworks.jukex.AttributeValue; public aspect Logging { /* Log all exceptions */ before() : (handler(Exception) || handler(SQLException)) && within(com.neoworks.jukex.sqlimpl.*) && !within(Logging) { String name = thisJoinPoint.getThis().getClass().getName(); Category log = Category.getInstance(name); log.error("Encountered exception in " + thisJoinPoint); } /* Log method entries to loadPlaylists and setUpdatedDate */ pointcut entries(): execution(void JukeXTrackStore.loadPlaylists(..)) || execution(void JukeXTrack.setUpdatedDate(java.util.Date)); before(): entries() { String name = thisJoinPoint.getThis().getClass().getName(); Category log = Category.getInstance(name); if (log.isDebugEnabled()) log.debug("Entering " + thisJoinPoint); } /* Log when null is returned from getAttributeValue */ after() returning(AttributeValue a) : execution(* JukeXTrack.getAttributeValue(..)) { String name = thisJoinPoint.getThis().getClass().getName(); Category log = Category.getInstance(name); if (a == null) log.warn("Cannot find attribute name."); } /* Log when delegating in getNextTrack */ before() : call(* *.delegateGetNextTrack(..)) && within(JukeXPlaylist) { String name = thisJoinPoint.getThis().getClass().getName(); Category log = Category.getInstance(name); log.warn("I'm spent, delegating..."); } } [Note: the AspectJ Development Tool Eclipse plug-in aided in writing this aspect.] CSE 503 - Homework 4 Problem 3 - Crosscutting Concerns Charlie Reis, 6-2-2004 3A) Identifying Crosscutting Concern In the com.neoworks.jukex.connectionpool package, I noticed that the primary purpose of ConnectionWrapper was to execute a handleIsClosed() check before each call to java.sql.Connection's public methods. It also has a small amount of functionality for auto commits and closing connections. The entire class represents a crosscutting concern where the connectionpool package wishes to impose a particular behavior on every method of every implementation of the java.sql.Connection interface. It is fragile in the sense that new methods added to the interface will require changes to ConnectionWrapper. 3B) Location of Concern The Connection concern is somewhat centralized, consisting of every public method in the ConnectionWrapper class. Despite being located in a single class, this concern is still crosscutting and not modularized, because the same check is duplicated across every one of Connection's numerous methods. The design decisions of the java.sql package authors influenced this design, since they did not provide an interface for listening to calls. (Of course, it would have been quite out of the ordinary to do so, but it would provide the ability to centralize the handleIsClosed concern.) 3C) Modularize the Concern It is indeed possible to modularize the handleIsClosed concern in AspectJ, and even the autoCommit behavior. The aspect to accomplish this is given below. It is worth noting that this almost entirely eliminates the need for the ConnectionWrapper class, except for the need to call wrapperClosed on the ConnectionPool from which the Connection was obtained. (If the aspect could learn which ConnectionPool a given Connection came from, this would handle all aspects of the ConnectionWrapper class.) import java.sql.*; import org.apache.log4j.Category; public aspect ConnectWrapper { private boolean isClosed = false; private boolean autoCommit = true; private void handleIsClosed() throws SQLException { if (isClosed) { throw new SQLException("Pooled connection is closed"); } } /* Call handleIsClosed before almost all public methods */ pointcut entries() : execution(* Connection.*(..)) && !execution(* Connection.close(..)) && !execution(* Connection.isClosed(..)) && !execution(* Connection.toString(..)); before() throws SQLException : entries() { handleIsClosed(); } /* Handle new autoCommit logic imposed in ConnectionWrapper */ after(Connection c) : execution(Connection+.new(..)) && target(c) { try { c.setAutoCommit(true); // Get the connection into a known state } catch (SQLException se) { Category log = Category.getInstance(thisJoinPoint.getThis().getClass().getName()); if (log.isDebugEnabled()) log.debug("Possible problem - could not setAutoCommit on real connection upon construction"); } } void around(Connection c, boolean ac) throws SQLException : execution(void Connection.setAutoCommit(boolean)) && args(ac) && target(c) { if (autoCommit == ac) return; try { proceed(c, ac); autoCommit = ac; } catch (SQLException e) { if ( e.getMessage().startsWith("SET CHAINED command not allowed within multi-statement transaction") ) { c.commit(); proceed(c, ac); } else { throw e; } } } boolean around() : execution(boolean Connection.getAutoCommit()) { return autoCommit; } } [Note: the AspectJ Development Tool Eclipse plug-in aided in writing this aspect.] CSE 503 - Homework 4 Problem 4 - Design Patterns Charlie Reis, 6-2-2004 4A) Singleton Design Pattern The com.neoworks.connectionpool.PoolManager class uses the Singleton design pattern. Relevant code snippets are given below, including the static instance field and getInstance() method and private constructors. It is worth noting the potential danger of calling the configure methods on lines 98 and 110, which have the ability to replace the singleton instance. If these are called after a module has a reference to an earlier PoolManager, multiple PoolManagers may be in use. Static singleton instance field and accessor method (lines 56, 76): /** * The single instance of this class. */ private static PoolManager instance = null; // ... /** * Return the static instance of this class. * * @return The single instance of this class, or null if there was a problem reading the properties file, setting up the connection pools and registering drives.. */ static synchronized public PoolManager getInstance() { if (instance == null) // If the singleton instance hasn't been initialised yet { try { instance = new PoolManager(); } catch (Exception e) { instance = null; e.printStackTrace(); } } return instance; } Private constructors (lines 124, 199): /** * Private constructor to implement singleton pattern. * * This constructor is called with no argument so it moves into 'guess * your own configuration' mode so we have to have a look around and * try to find some configuration for ourselves. */ private PoolManager() throws IOException, ClassNotFoundException, InstantiationException,IllegalAccessException, SQLException // ... /** * Private constructor to create and configure this object from a DOM * * @param configElement The DOM Element to use for configuration */ private PoolManager( Element configElement ) // ... [Note: no tools were necessary to identify this pattern, as it was evident from previous browsing.] 4B) Strategy Design Pattern The com.neoworks.jukex.tracksource.filter package employs the Strategy design pattern. The TrackFilter interface and its sub-interface, AttributeTrackFilter, both participate as the Strategy, declaring an interface common to all supported algorithms. The ConcreteStrategy participants include AttributeEqualityTrackFilter, AttributeRegexTrackFilter, and AttributeStartsWithTrackFilter, all of which implement the AttributeTrackFilter interface. The com.neoworks.jukex.client.html.standard.JukeXServlet class participates as the Context, instantiating a particular TrackFilter based on the contextual parameters. The interface definitions and implementations are all straightforward Java code in the above classes, so code snippets would be redundant. A code snippet of the context is provided below, illustrating the use of the Strategy static type for a variable (ie. TrackFilter) and conditional instantiations of each ConcreteStrategy. Line 273 of JukeXServlet.doGet(): TrackFilter f = null; String filterType = req.getParameter("filter.type"); try { if (filterType.equals("equals")) { f = new AttributeEqualityTrackFilter(a,av); } else if (filterType.equals("startswith")) { f = new AttributeStartsWithTrackFilter(a,av); } else if (filterType.equals("matches")) { f = new AttributeRegexTrackFilter(a,av); } FilterPipelineElement fpe = (FilterPipelineElement)pipeline.get(index); fpe.addFilter(f); pipeline.storePipeline(); } catch (PatternSyntaxException e) { log.warn("Could not compile regex" , e); } [Note: Context participant was identified using Vibha's "DeFacto Interfaces" Eclipse plug-in.] CSE 503 - Homework 4 Problem 5 - Software Engineering Tools Charlie Reis, 6-2-2004 Tools used in investigation: 1) Eclipse Eclipse and its built-in features were the primary tools that I used for this assignment, along with several plug-ins described below. For me, Eclipse's most useful features for code comprehension included the outline view, type hierarchy view, and the call hierarchy view. Being able to right click on a method and view all call sites (transitively) was particularly useful in many contexts. Eclipse's main drawbacks are its enormous size (causing even my brand new PowerBook to thrash) and its high learning curve-- after using the environment for over a year, I still lack an understanding of most of its menu options. There are probably many more features which would have been useful for this analysis if I had been aware of them. 2) Vibha's Eclipse Plug-in This Eclipse plug-in was installed in the third floor Software Engineering lab, and I had been introduced to it as part of a user study. I could not get the Dependencies view to work, but the Type Assumptions, DeFacto Interfaces, and Information Hiding views were all useful for this homework. Type Assumptions helped reveal violations of information hiding, though it was not always clear what it was catching, since it did not identify all casts. The DeFacto Interfaces view was useful for determining coupling relationships, though it was limited by not showing indirect uses (eg. uses of a class were sometimes masked by the Factory pattern). The Information Hiding view was only useful in terms of the "other classes used" feature, which helped reveal coupling relationships. On the whole, the main drawback was not being able to jump to locations in the code by clicking on the results, or even view the line numbers. 3) FindBugs http://findbugs.sourceforge.net/ The FindBugs tool was developed by Bill Pugh and David Hovemeyer at the University of Maryland as a static analysis tool to look for "bug patterns" that are likely to be errors. It was quite useful for identifying violations of information hiding, though it is limited by being neither sound nor complete. That is, it does not catch all errors, and it produces many false positives which may not in fact be errors. 4) DrJava Eclipse Plug-in http://drjava.org/eclipse.shtml This plug-in provides an Interactions Pane for interactive evaluation of Java code in context of the current projects. I used this mainly to tell if two classes were instances of each other. It is worth noting that the main strengths of this tool (ie. creating and interacting with objects) were not as useful in this project, given the large amount of setup and context (eg. a databse) that would be required to use most JukeX classes. (Disclaimer: I wrote this plug-in last year.) 5) AspectJ Eclipse Plug-in http://eclipse.org/aspectj/ The AspectJ plug-in was remarkably useful for learning aspects, primarily through its Outline view which showed where particular advice actually applied. The syntax highlighting was useful as well, though the auto-complete often gave erroneous suggestions, and I encountered bugs in the plug-in causing Eclipse to crash. The main limitation I found in the tool was not being able to help describe a new pointcut without already knowing the language. (It would be nice to have a way to describe what I want to capture and have the plug-in suggest a pointcut.) 6) Grep and shell scripting Though grep provides purely syntactic search without any semantic knowledge of program structure, it can still be useful remarkably often, given programmer's common naming conventions. Once a programmer is familiar with grep, it can be a very fast tool to use to find information, but it can sometimes miss information or provide far too many irrelevant details due to its syntactic nature.