The Smoke and Mirrors Behind Oddjob Drag And Drop

For those that read my last post on Oddjob’s server Drag n’Drop capabilities here’s the smoke and mirrors, sorry configuration, behind the trick.

First the XML. There are two server configuration files, server1.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<oddjob id="this">
    <job>
        <sequential>
            <jobs>
                <jmx:server root="${server-jobs}" xmlns:jmx="http://rgordon.co.uk/oddjob/jmx"/>
                <oddjob id="server-jobs" name="Server Oddjob">
                    <configuration>
                        <xml>
                            <oddjob>
                                <job>
                                    <folder>
                                        <jobs>
                                            <echo id="echo-job"><![CDATA[Hello from ${server.name}]]></echo>
                                        </jobs>
                                    </folder>
                                </job>
                            </oddjob>
                        </xml>
                    </configuration>
                </oddjob>
                <wait/>
            </jobs>
        </sequential>
    </job>
</oddjob>

This has the original Job in it. Some points to note:

  • Although the server can export any job as the root, it is only possible to configure the configuration belonging to a ConfigurationOwner which Oddjob is. This is why we export a nested Oddjob as the root node.
  • The nested Oddjob is configured with in-line XML provided using the <xml> type. This is just laziness on my part but it does result in a configuration that can’t be saved back into original file from the client. If the nested Oddjob was also configured with a file, then changes could be saved from the client back into that file.
  • It isn’t possible to paste onto a nested Oddjob, because we have to decide whether to treat it as the child of it’s parent or as a parent in it’s own right, and in Oddjob Explorer we treat it as the child of a parent, but give it a ‘Design Inside’ action to be able to get at that internal configuration. Having to go this circulus route wouldn’t look slick in the demo which is why I introduced a <folder> who’s children can be dragged about but any parent such as <sequential> or <parallel> would have done too.
  • Oddjob will automatically terminate when it hasn’t got anything else to do – i.e. no more running threads. Because our jobs don’t do anything we need something to keep Oddjob alive, which is why there is a final <wait> job. This wouldn’t be needed if we had something such as a timer to keep Oddjob alive instead.

Then there is server2.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<oddjob id="this">
    <job>
        <sequential>
            <jobs>
                <jmx:server root="${server-jobs}" xmlns:jmx="http://rgordon.co.uk/oddjob/jmx"/>
                <oddjob id="server-jobs" name="Server Oddjob">
                    <configuration>
                        <xml>
                            <oddjob>
                                <job>
                                    <folder id="job-folder"/>
                                </job>
                            </oddjob>
                        </xml>
                    </configuration>
                </oddjob>
                <wait/>
            </jobs>
        </sequential>
    </job>
</oddjob>

This is very similar to the previous configuration except there is no echo job in the folder and the folder has been given an id so it is accessible from the code.

And the client.xml:

<oddjob>
    <job>
        <sequential>
            <jobs>
                <jmx:client id="client1" name="Client 1" url="service:jmx:rmi:///jndi/rmi://localhost:9101/jmxrmi" xmlns:jmx="http://rgordon.co.uk/oddjob/jmx"/>
                <jmx:client id="client2" name="Client 2" url="service:jmx:rmi:///jndi/rmi://localhost:9102/jmxrmi" xmlns:jmx="http://rgordon.co.uk/oddjob/jmx"/>
            </jobs>
        </sequential>
    </job>
</oddjob>

This is quite straight forward – but note how Oddjob isn’t always very cleaver when it writes out the XML namespaces!

And finally here are the commands to kick off the servers:

java -Dcom.sun.management.jmxremote.port=9101 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dserver.name=Server1 -jar run-oddjob.jar -f server1.xml

and

java -Dcom.sun.management.jmxremote.port=9102 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dserver.name=Server2 -jar run-oddjob.jar -f server2.xml

And that’s it – so now you too can impress friends by dragging an XML bean configuration between two servers!

Posted in Oddjob | Tagged | Leave a comment

Why I Wrote Yet Another Java Bean Framework

With so many Java Bean XML and dependency injection frameworks out there – did I really need to write another? Am I suffering from some terrible ‘Not Invented Here’ syndrome? In this post I’m hoping to persuade myself that I’m not, and hopefully I can persuade you that it might be worth a look too.

It all came down to Drag and Drop. I wrote my own framework because I couldn’t find another that allowed beans to be Cut, Pasted and Configured in place – while running – no restart required!

Here it is working between two server processes, scroll quickly for that Flip Book animation effect!

Oddjob Drag and Drop 1Oddjob Drag and Drop 2Oddjob Drag and Drop 3What’s going on? – I’m dragging the XML configuration for a bean from one server process to another. Here are those server processes:

Oddjob Drag and Drop ServersNow I’m going to drag that job straight into this post…

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<echo id="echo-job"><![CDATA[Hello from ${server.name}]]></echo>

…and no I didn’t cheat!

Here is that server drag and drop again, except in code:

public class CutAndPaste {

	public static void main(String... args) throws Exception {

		Oddjob oddjob = new Oddjob();
		oddjob.setConfiguration(new XMLConfiguration(new File("client.xml")));

		oddjob.run();

		OddjobLookup lookup = new OddjobLookup(oddjob);

		DragPoint drag1 = lookup.lookup("client1/server-jobs", ConfigurationOwner.class
				).provideConfigurationSession().dragPointFor(
						lookup.lookup("client1/server-jobs/echo-job"));

		String copy = drag1.copy();
		drag1.cut();

		DragPoint drag2 = lookup.lookup("client2/server-jobs", ConfigurationOwner.class
				).provideConfigurationSession().dragPointFor(
						lookup.lookup("client2/server-jobs/job-folder"));

		drag2.paste(0, copy);

		Runnable job = lookup.lookup("client2/server-jobs/echo-job", Runnable.class);

		job.run();

		oddjob.destroy();
	}
}

I also run the job in its new location – just for good measure.

Now many of you will be saying ‘Why would I want anyone dragging around my applications configuration!’, and yes, in production this is quite dangerous and most of the time you probably do want all your beans and their dependencies locked down.

But sometimes it might be really useful to have a bit of flexibility with your remote components configuration, and for those sometimes I wrote a cut and paste Java Bean XML based dependency injection framework.

Now I’m expecting lots of feedback telling me that XYZ framework will do this already, and I’m looking forward to it, and I don’t mind – because there is another reason I wrote the framework, the main reason… I loved every minute of it!

Posted in Oddjob | Tagged | 1 Comment

Passing a Parameter from Oddjob to Spring Batch

I wrote previously about scheduling a Spring Batch application from Oddjob. Here I’ll demonstrate how to pass the scheduled date, as a parameter, from Oddjob to Spring Batch.

Passing a parameter to our wrapped Spring Batch application needs a few lines of code – but not many.

First I add a date property to my previous launcher and add the highlighted lines to pass the property into Spring Batch:

public class MyLauncher2 implements Callable<Integer> {

	private Date date;

	public Integer call() throws Exception {

		ConfigurableApplicationContext context =
				new ClassPathXmlApplicationContext("classpath:/launch-context2.xml");

		try {
			JobLauncher launcher = context.getBean(JobLauncher.class);
			Job job = (Job) context.getBean("job2");

			Map<String, JobParameter> parameters =
					new HashMap<String, JobParameter>();

			parameters.put("batch-date", new JobParameter(date));

			JobExecution jobExecution = launcher.run(job,
					new JobParameters(parameters));

			if (jobExecution.getExitStatus() == ExitStatus.FAILED) {
				return 1;
			}
			else {
				return 0;
			}
		}
		finally {
			context.close();
		}
	}

	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}

	@Override
	public String toString() {
		return "My Launcher2";
	}
}

The new date property gets set by Oddjob from the timer like this:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<bean class="rob.MyLauncher2" id="launcher">
    <date>
        <value value="${timer.lastDue}"/>
    </date>
</bean>

Then I modify the Spring Tool Suite provided ExampleItemReader.java to accept a date parameter:

public class ExampleItemReader2 implements ItemReader<String> {

	private String[] input = {"Hello world!"};

	private int index = 0;

	private Date date;

	/**
	 * Reads next record from input
	 */
	public String read() throws Exception {
		if (index < input.length) {
			return input[index++] + " at " + new SimpleDateFormat(
					"dd-MMM-yy HH:mm").format(date);
		}
		else {
			return null;
		}
	}

	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}
}

Here’s the Spring configuration for my new bean:

    <bean id="reader2" class="my.scratch.springbatch.ExampleItemReader2" scope="step">
        <property name="date" value="#{jobParameters['batch-date']}"/>
    </bean>

And here’s what it looks like in Oddjob:

Oddjob with Spring Batch and a Parameter.OK – so I’ve also done some other things. I’ve removed the HSQL embedded database from the Spring Batch Project and used Oddjob’s own HSQL plugin instead. This allows the database to be available between runs as would be the case for a real world deployment. I’ve also collected together some utility jobs in the JobFolder. These won’t run automatically but are handy to be manually run when required – this is how I installed the Spring Batch schema.

And that’s pretty much it. All the source code and Oddjob configuration are in the Oddjob Oddments Directory on Sourceforge.

Posted in Oddjob, spring-batch | Tagged , | Leave a comment

Scheduling Spring Batch with Oddjob

Spring Batch is not a scheduling framework… It is intended to work in conjunction with a scheduler, not replace a scheduler’ – That’s what the Spring Batch manual says. But as luck would have it, Oddjob is a scheduler, and it can work in conjunction with Spring Batch very very easily.

A Sample Spring Batch Project

If you don’t have a Spring Batch project lying around the easiest way to get one is to follow the instructions on the Spring Batch Getting Started page. If you are having trouble with this you might be interested in The Fun I had Getting Started with Spring Batch!

Method 1 – Launch a Batch File

Very Simple. Here’s a screen shot:

Spring Batch Oddjob using the Command LineAnd here’s the configuration:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<oddjob>
    <job>
        <sequential>
            <jobs>
                <variables id="vars">
                    <base>
                        <file file="${user.home}/scratch/spring-batch"/>
                    </base>
                    <jar>
                        <files files="${vars.base}/target/*.jar"/>
                    </jar>
                    <cp>
                        <files>
                            <list>
                                <value value="${vars.jar}"/>
                                <files files="${vars.base}/target/lib/*.jar"/>
                            </list>
                        </files>
                    </cp>
                </variables>
                <scheduling:timer xmlns:scheduling="http://rgordon.co.uk/oddjob/scheduling">
                    <schedule>
                        <schedules:weekly from="MONDAY" to="FRIDAY" xmlns:schedules="http://rgordon.co.uk/oddjob/schedules">
                            <refinement>
                                <schedules:daily from="07:00">
                                    <refinement>
                                        <schedules:interval interval="00:30"/>
                                    </refinement>
                                </schedules:daily>
                            </refinement>
                        </schedules:weekly>
                    </schedule>
                    <job>
                        <exec><![CDATA[java -cp ${vars.cp}
org.springframework.batch.core.launch.support.CommandLineJobRunner
classpath:/launch-context.xml job1]]></exec>
                    </job>
                </scheduling:timer>
            </jobs>
        </sequential>
    </job>
</oddjob>

Although this looks like a big mess of XML, Oddjob’s designer user interface meant I was able to create this configuration in only a few minutes. Here’s what’s going on:

  1. The Variables job sets up the the Classpath which is all the Jar files in the target and target/lib directories.
  2. The Exec Job runs the Spring Batch CommandLineRunner using the previously defined class path.
  3. The Timer runs the Exec Job every half an hour between 07:00am and midnight, Monday to Friday.

The last 1000 lines of console output are captured by Oddjob and that’s what we can see in the right hand panel.

Method  2 – Embed in Oddjob with A Wrapper Class

Here’s a very simple wrapper class for our test project:

public class MyLauncher implements Callable<Integer> {

	public Integer call() throws Exception {

		ConfigurableApplicationContext context =
				new ClassPathXmlApplicationContext("classpath:/launch-context.xml");

		try {
			JobLauncher launcher = context.getBean(JobLauncher.class);
			Job job = (Job) context.getBean("job1");

			JobExecution jobExecution = launcher.run(job, new JobParameters());

			if (jobExecution.getExitStatus() == ExitStatus.FAILED) {
				return 1;
			}
			else {
				return 0;
			}
		}
		finally {
			context.close();
		}
	}

	@Override
	public String toString() {
		return "My Launcher";
	}
}

And here it is running:

The configuration has changed a little:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<oddjob id="this">
    <job>
        <sequential>
            <jobs>
                <variables id="vars">
                    <base>
                        <file file="${user.home}/scratch/spring-batch"/>
                    </base>
                    <classes>
                        <files files="${vars.base}/target/classes"/>
                    </classes>
                    <cp>
                        <files>
                            <list>
                                <value value="${vars.classes}"/>
                                <files files="${vars.base}/target/lib/*.jar"/>
                            </list>
                        </files>
                    </cp>
                </variables>
                <scheduling:timer xmlns:scheduling="http://rgordon.co.uk/oddjob/scheduling">
                    <schedule>
                        <schedules:weekly from="MONDAY" to="FRIDAY" xmlns:schedules="http://rgordon.co.uk/oddjob/schedules">
                            <refinement>
                                <schedules:daily from="07:00">
                                    <refinement>
                                        <schedules:interval interval="00:30"/>
                                    </refinement>
                                </schedules:daily>
                            </refinement>
                        </schedules:weekly>
                    </schedule>
                    <job>
                        <oddjob file="${this.dir}/RunFromOddjobInner.xml" id="adhoc-oddjob">
                            <classLoader>
                                <url-class-loader>
                                    <files>
                                        <value value="${vars.cp}"/>
                                    </files>
                                </url-class-loader>
                            </classLoader>
                        </oddjob>
                    </job>
                </scheduling:timer>
            </jobs>
        </sequential>
    </job>
</oddjob>

And:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<oddjob>
    <job>
        <bean class="rob.MyLauncher" id="launcher"/>
    </job>
</oddjob>

We’re using a nested Oddjob to build the class path. This is just one of many ways to let Oddjob know where classes for Job’s are, others are described in the Developer Guide. Although it’s a nested Oddjob, it still all runs in just one JVM.

Oddjob keeps the last 1000 lines of log output from a component for display purposes, again you can see this in right hand panel. The format is different to the console output in method 1, because it’s Oddjob that’s dictating the pattern, not Spring Batch.

And that’s how to schedule a Spring Batch application from Oddjob. All the source code is available from the Oddjob Oddments Directory on Sourceforge.

Posted in Oddjob, spring-batch | Tagged , | 1 Comment

Oddjob 1.1 Released

Version 1.1. of Oddjob is now available.

This release includes:

A new JSF AJAX Web Front End which replaces the old Struts version.

Oddjob JSF/Ajax Browser View

Oddjob JSF/Ajax Browser View

Support for providing a job as a Callable as well as a Runnable.

Here’s a Callable:

import java.util.concurrent.Callable;

public class HelloBean implements Callable<Integer> {

	private String time;

	@Override
	public Integer call() throws Exception {

		System.out.println("Hello at " + time + ".");

		return 0;
	}

	public void setTime(String time) {
		this.time = time;
	}

	@Override
	public String toString() {
		return "My Callable";
	}
}

And here it is running in Oddjob:

Callable Bean Demo

From this configuration:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<oddjob>
    <job>
        <scheduling:timer id="timer" xmlns:scheduling="http://rgordon.co.uk/oddjob/scheduling">
            <schedule>
                <schedules:daily from="07:00" xmlns:schedules="http://rgordon.co.uk/oddjob/schedules">
                    <refinement>
                        <schedules:interval interval="00:02"/>
                    </refinement>
                </schedules:daily>
            </schedule>
            <job>
                <sequential>
                    <jobs>
                        <properties>
                            <values>
                                <format date="${timer.lastDue}" format="HH:mm" key="formatted.time"/>
                            </values>
                        </properties>
                        <bean class="examples.HelloBean" time="${formatted.time}"/>
                    </jobs>
                </sequential>
            </job>
        </scheduling:timer>
    </job>
</oddjob>

The ability to Force a Failed Job to Complete.

If a job fails and you work around it manually you can now force that job to complete so dependent jobs can trigger.

Oddjob Forceable

Posted in Oddjob | Tagged | Leave a comment