My favorites | Sign in
Project Logo
             
Search
for
Updated Nov 09, 2007 by vmatters
Labels: Featured
BroadwayAndSpring  

Broadway and the Spring Framework

Broadway was designed as a component framework. Therefore, it plays really well with component container technologies such as Spring. If you don't know what Spring is about, visit springframework.org. It's beyond the scope of this project to introduce you to Spring. If you use Spring, you will enjoy creating monitors with Broadway. Because of Broadway's design, it's really easy to wire together the key Broadway Components to create monitors in little time.

A Directory Synchronizer

To illustrate how to create monitors with Broadway and Spring, we will create a directory synchronizer. As the name implies, we will setup Broadway to monitor two directories (one primary and a secondary). The monitor will keep the secondary directory synchronized with the primary. A monitor will observe the primary directory for size changes. When the sizes of the two directories differ, files from the primary are copied to the secondary automatically.

This can be easily accomplished using Broadway and Spring. The only piece of code that is required is the logic for the action component which will copy files from the primary to the secondary directory. In this case, when the monitor detects the condition (directory sizes differ), it will execute the provided action. As we will see, the logic for the Action can be captured in Groovy scripts or through Groovy Beans. If you don't like scripting language, the Action can be implemented using Java. The rest of this page shows how to wire the Broadway components using the Spring framework.

Wiring Broadway Components in Spring

Since Broadway components follow simple JavaBean notations, it's easy to start wiring them together in Spring. In order to build monitors with Broadway and Spring, here are the components you will need to connect together to create the synchronizer.

Observed Resource

This portion of the Spring config file sets up Observed Objects. You may wish to capture states/conditions to be observed using a domain-specific component as it is done here (the observed component represents a directory structure). In the Spring mapping, we have two observed directory components wired for to be synchronized. The primary directory is ./dir1 and the secondary directory is ./dir2. The system will copy files from primary to secodary when it detects size differences.

	<!-- ====================== Observed Resources ======================= -->
	<bean id="firstDir"
		class="org.broadway.demo.spring.ObservedDirectory">
		<property name="directoryName" value="./dir1" />
	</bean>
	<bean id="secondDir"
		class="org.broadway.demo.spring.ObservedDirectory">
		<property name="directoryName" value="./dir2" />
	</bean>

Click to view full SpringConfigXml file.

The Java code for the Observed Directory component is simple. It provides a facade to the a file system and expose specific states to be observed as a monitoring condition. This sort of observed objects are not required. However, they can expose richer set of domain-specific data to be observed by your monitors. For instance, we can include data caching or fancier logic to retrieve the observed data never reveal that complexity to the rest of the system.

Note that you can realize the same results without using Observed Objects by carefully crafting your monitoring expressions. However, your code will be much more verbose and less reusable.

Here is the code for the ObservedDirectory class. As you can see it is very simple, it returns the name of the observed directory and its size using Apache Commons IOlibrary. You can also detect whether the directory is empty or not. So the class shields the rest of the code from the complexity of dealing with IO system.



    // Java code for ObservedDirectory
    public class ObservedDirectory {
	private String root;

	public String getRoot() {
		return root;
	}

	public void setDirectoryName(String root) {
		this.root = root;
	}
	public String getDirectoryName(){
		return root;
	}
	
	public boolean isEmpty(){
		long size = org.apache.commons.io.FileUtils.sizeOfDirectory(new File(getDirectoryName()));
		return (size > 0) ? false : true;
	}
	
	public long getDirectorySize(){
		return FileUtils.sizeOfDirectory(new File(getDirectoryName()));
	}
	
    }

Monitor

This component monitors state changes expressed by the "monitorExpression" property. It observed for a pre-defined condition and when the predetermined condition is detected, the monitor will execute the associated "action" specified by the Action property. This is a required component.

	<!-- ======================= Monitor Setup ========================== -->
	<bean id="monitor" class="org.broadway.monitor.BeanMonitor">
		<property name="monitorExpression"> 
			<value><![CDATA[params.firstDir.directorySize != params.secondDir.directorySize]]></value>
		</property>
		
		<property name="action" ref="scriptedAction"/>
		<property name="resourceCollector">
			<bean
				class="org.broadway.monitor.MappedResourceCollector">
				<property name="resources">
					<map>
						<entry key="firstDir"  value-ref="firstDir" />
						<entry key="secondDir" value-ref="secondDir" />
					</map>
				</property>
			</bean>
		</property>
	</bean>

The monitor setups three vital pieces of information needed to do its job:

Click to view full SpringConfigXml file.

Action

The Action component embodies the logic that is executed when a desired condition caused by state changes in observed objects is reached. This example we will show three ways you can create and wire action components for your monitor.

ScriptedAction Component

The first action component we will use is the Broadway-provided ScriptedAction component. This component lets you execute actions written in a scripting language. It uses Broadway's Script Engine component (internally) which lets you to use any scripting language supported by Apache Jakarta BSF. The setup here uses a Groovy script called synchronieDir.groovy to copy files between directories. Below is the Spring wiring.

	<bean id="scriptedAction"
		class="org.broadway.monitor.ScriptedAction">
		<property name="scriptFileLocation" value="scripts/synchronizeDir.groovy" />
	</bean>

Note that the script file location property specifies where the script is located. Broadway will search for the file on the file system. If not found, it will also search the class path for the file. In this example, the file is placed on the class path.

synchronizeDir.groovy

Yes, Groovy is beautiful tang! In fewer than 10 lines, we are synchronizing two directories (albeit rudimentary). If you are interested in Groovy, visit http://groovy.codehaus.org/.

Notice that the ScriptedAction component that runs the script exposes contextual data that is made available at runtime. Here, the script is accessing values from the actual objects that are being observed (firstDir and secondDir) to get the directory names at runtime. Other objects exposed to the script at runtime include a reference to the monitor object, result of the monitor expression evaluation, and a anything else added to the context.

def dirPath1 = resources.firstDir.directoryName;
def dirPath2 = resources.secondDir.directoryName;

print "***** Synchronizing Directories [Mirror dir2 to Dir1] *****\n";

def File dir1 = new File(dirPath1);
dir1.eachFile {origFile ->
	def origFileName = origFile.name;

	def destFile = new File(dirPath2+ File.separator + origFileName);
	
	if(!destFile.exists() || (destFile.lastModified() < origFile.lastModified())){
		print "Synching file File $origFile.name ...\n";
		def fis = origFile.newInputStream();
		new FileOutputStream(destFile).withStream {fos ->
			fos << fis;
		}
	}
}

Using Spring's Dynamic Language Support

The second Action component example shows how to use Spring's support for dynamic language. This feature was introduced in Spring starting with version 2.0. In Spring, you can wire a Groovy (JRuby or BSH) Bean into a Spring context as if it is a regular Java POJO. This implies that you can inject values into your Groovy bean and wire with other beans in Spring context. If you want to learn more about Spring's Dyanamin Language Support, go to Spring Dyanamic Language Support online documentation.

Here is the Spring configuration's required xml declaration to use the dynamic language support. You must have the proper XML schema declaration or your bean will not be mounted properly in the Spring context.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:lang="http://www.springframework.org/schema/lang"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
	http://www.springframework.org/schema/lang
	http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">

...

</beans>

To wire your Groovy Bean, use the following notation. Note the use of the <lang:groovy> tag to wire in the Groovy bean. The script-source attribute specifies the location of the Groovy script file. The file can be on the class path or on located anywhere on the file system. Using this tag, you can also specifies a refresh rate at which the file is will be recompiled (if necessary).

Spring also supports inline script definition as well where you place your code directly in within the tags (not shown here).

    <lang:groovy id="groovyAction" script-source="classpath:scripts/SyncAction.groovy"/>

Click to view full SpringConfigXml file.

SyncAction.groovy

The Groovy Bean option implements the Action interface directly to carry out the logic for the synchronizer. If you don't care about the Spring dependency, this is actually a better option then using ScpriptedAction. However, you need to do some tedious class casting to get to the values in the context object. Here is what the Groovy Action class looks like:

class SyncAction implements Action{
	public Object execute(Context ctx){
		HashMap resources = (HashMap)ctx.getValue(MonitorConstants.MonitorKeys.RESOURCE_COLLECTION);
		
		String dirPath1 = resources["firstDir"].directoryName;
		String dirPath2 = resources["secondDir"].directoryName;
		
		File dir1 = new File(dirPath1);
		dir1.eachFile {origFile ->
			String origFileName = origFile.name;
		
			File destFile = new File(dirPath2+ File.separator + origFileName);
			
			if(!destFile.exists() || (destFile.lastModified() < origFile.lastModified())){
				print "Synching file File $origFile.name ...\n";
				def fis = origFile.newInputStream();
				new FileOutputStream(destFile).withStream {fos ->
					fos << fis;
				}
			}
		}

	}
}

This Groovy class is functionally equivalent to the previous Groovy script. As you can see, the class implements the Action interface. Spring requires that your Groovy class implements an interface that is found on the classpath in order for your Groovy bean to function as a Spring POJO. The implemented method, execute(), gets an instance of the monitor context. The Groovy class has access to the same context information available to the ScriptedAction component. However, you are responsible for digging it out of the context object.

Conclusion

That's it! Simply run the application and it will keep the specified directories synchronized. As you can see, it does not take much to get started with Spring and Broadway. You have several options to implement your code including dynamic languages such as Groovy or Java. You can run the code yourself by downloading (see below).


Sign in to add a comment
Hosted by Google Code