This Blog entry is part two of the series; writing JBoss Customer Services. This Blog assumes that you have completed
all the coding tasks detailed in the previous blog entry. To review the previous Blog entry; we set-up an Eclipse development project and coded an MBean interface and ServiceMBeanSupport class. Next, we created and modified the jboss-service.xml file for our BlogExampleService. An Ant build.xml file was used to compile, assemble, and deploy the SAR File. Once the SAR File was deployed, the JBoss JMX-Console was used to access our service and invoke lifecycle methods. Now we are ready to extend the JBoss service to utilize other JBoss’s Scheduling services.
JBoss Scheduling Service
The JBoss documentation describes the JBoss scheduler service as follows:
Java includes a simple timer-based capability through the java.util.Timer and java.util.TimerTask utility classes. JMX also includes a mechanism for scheduling JMX notifications at a given time with an optional repeat interval as the java.management.timer.TimerMBean agent service.
JBoss includes two variations of the JMX timer service in the org.jboss.varia.
scheduler.Scheduler and org.jboss.varia.scheduler.ScheduleManager MBeans. In essence, the Scheduler Instance runs a scheduling service for any Schedulable instances. For more information on the Scheduling service please refer to the JBoss documentation.
Implement Schedulable Interface
In order to create a schedulable implementation, the ServiceMBeanSupport class (BlogExampleService) needs to implement the org.jboss.varia.scheduler.Schedulable class.
Modify the BlogExampleService java program to implement the Schedulable interface:
public class BlogExampleService extends ServiceMBeanSupport
implements BlogExampleServiceMBean, Schedulable {
Eclipse will help to organize your class by importing org.jboss.varia.
scheduler.Schedulable from the scheduler-plugin.jar (part of the projects build path). You will need to override the abstract perform method and add a logging entry:
public void perform(Date inDate, long repetitionCountdown) {
_log.info("perform() date: " + inDate +
“Repetition Countdown " + repetitionCountdown);
}
Add the following Scheduler MBean configuration details to the jboss-service.xml file (insert this text after the BlogExampleService configuration):
<mbean code="org.jboss.varia.scheduler.Scheduler"
name="blog.example:service=BlogExampleScheduler">
<attribute name="StartAtStartup">false</attribute>
<attribute name="SchedulableClass">
blog.example.services.BlogExampleService
</attribute>
<attribute name="SchedulableArguments">
</attribute>
<attribute name="SchedulableArgumentTypes">
</attribute>
<attribute name="InitialStartDate">NOW</attribute>
<attribute name="SchedulePeriod">60000</attribute>
<attribute name="InitialRepetitions">3</attribute>
<depends>blog.example:service=BlogExampleService</depends>
</mbean>
Jboss-service.xml Scheduler MBean Attributes:
The Scheduler MBean is named BlogExampleScheduler under the blog.example domain. The MBean has the following attributes:
• Start scheduler on startup = No
• Class to call = blog.example.services.BlogExampleService
• Constructor argument = None
• Constructor argument types = None
• When to start Scheduler – Now ( this translates into one second after startup)
• Interval period between scheduled call = 60,000 milliseconds (1 minute)
• Number of times the scheduler will invoke the target callback (BlogExampleService.perform()) = 3 times
• The BlogExampleScheduler depends on the creation of the BlogExampleService.
Deploy the BlogExampleScheduler Service
As we discussed in Part I, JBoss services are deployed as a SAR Files. To compile, assemble, and deploy the SAR File, run the deploy target of the build.xml file. The JBoss console output should look like this:
Buildfile: C:\psBlogs\blog-example\build.xml
compile:
[javac] Compiling 1 source file to C:\psBlogs\blog-example\build
dist:
[copy] Copying 1 file to C:\psBlogs\blog-example\build
[jar] Building jar: C:\psBlogs\blog-example\dist\blog_example.sar
deploy:
[copy] Copying 1 file to C:\jboss-4.0.3SP1\server\default\deploy
BUILD SUCCESSFUL
Total time: 5 seconds
Start the JBoss Console
Enter http://localhost:8080/ in your browser. Next, select the jmx-console hyperlink under the JBoss Management category. The JMX Agent View page will display the registered service domains; including the blog.example domain.
Start the Scheduler
Since the BlogExampleScheduler’s StartAtStartup attribute is set to false, the scheduler must be started manually using the JBoss JMX-Console.
1. Select the service=BlogExampleScheduler link under the blog.example domain.
2. Scroll down to the BlogExampleScheduler operations.
3. Press the Invoke Button under the startScheduler() operation.
JBoss console output
As the scheduler runs, the JBoss output will capture the following log entries:
[BlogExampleService] perform() date: Fri Apr 14 07:27:10 EDT 2006 Repetition Countdown 2
[BlogExampleService] perform() date Fri Apr 14 07:27:10 EDT 2006 Repetition Countdown 1
[BlogExampleService] perform() date: Fri Apr 14 07:27:10 EDT 2006 Repetition Countdown 0
Notice that the Repetition Countdown is decrementing each time the perform method is called.
Dealing with Constructor Parameters
To simulate a service constructor that has parameters, we will add a parameterized constructor to the BlogExampleService class.
Modify BlogExampleService.java as follows:
1. Add two private class variables:
private String firstName;
private int value;
2. Add assessors and mutators for the new variables (e.g. getFirstName(), etc)
3. Create a parameterized constructor for the BlogExampleService java program:
// add parameterized constructor
public BlogExampleService(String firstName, int value) {
this. firstName = firstName;
this.value = value;
}
4. Since you have created a parameterized constructor, you will also need to create an empty constructor.
// add empty constructor
public BlogExampleService() {}
Next edit the jboss-service.xml file to implement the new parameterized constructor. Modify the SchedulableArguments and SchedulableArgumentTypes attributes as follows:
<attribute name="SchedulableArguments">
firstName, value
</attribute>
<attribute name="SchedulableArgumentTypes">
java.lang.String, int
</attribute>
The SchedulableArguments attribute defines the constructor arguments. This comma separated list must map to the parameter list of the constructor. The SchedulableArgumentTypes attribute defines the argument type for the list of parameters detailed in the SchedulableArguments. Since firstName is of type String and value is of type int, the SchedulableArgumentTypes list is java.lang.String, int.
To validate that the parameter list is implemented, modify the perform method in BlogExampleService.java program as follows:
_log.info("perform() date: " + inDate + " Repetition Countdown " + repetitionCountdown + " name= " + getFirstName() + " value = " + getValue());
Deploy the BlogExampleScheduler Service
Run the deploy target of the build.xml file to compile, assemble, and deploy the SAR File.
Start the JBoss Console
Enter http://localhost:8080/ in the browser. Select the jmx-console hyperlink under the JBoss Management category. The JMX Agent View page will display the registered service domains; including the blog.example domain. You will now see two services:
• service=BlogExampleScheduler
• service=BlogExampleService
Enter the Constructor Parameters
Select the service=BlogExampleScheduler link under the blog.example domain. Under the List of MBean Attributes, enter a string and an integer in the SchedulableArguments Value textbox (remember the comma). As an example:
BlogExample, 100
Press the Apply Changes button to commit the changes.
Start the Scheduler
Start the Scheduler Service from the JBoss JMX-Console.
1. Select the service=BlogExampleScheduler link under the blog.example domain.
2. Scroll down to the BlogExampleScheduler operations.
3. Press the Invoke Button under the startScheduler() operation.
JBoss console output
As the scheduler runs, the JBoss output will capture the following log entries:
[BlogExampleService] perform() date: Fri Apr 14 07:47:10 EDT 2006 Repetition Countdown 2 firstName= BlogExample value = 100
[BlogExampleService] perform() date: Fri Apr 14 07:48:10 EDT 2006 Repetition Countdown 1 firstName= BlogExample value = 100
[BlogExampleService] perform() date: Fri Apr 14 07:49:10 EDT 2006 Repetition Countdown 0 firstName= BlogExample value = 100
Notice that the string and int value that were entered in the SchedulableArguments Value textbox are displayed in the output.
Exception
Return to the JBoss JMX-Console and select the service=BlogExampleScheduler link. Enter BlogExample, hundred in the SchedulableArguments Value textbox. Press the Apply Changes button to commit the changes. Now start the scheduler by pressing the invoke button for the startSchedule method. Since the parameter values are invalid (hundred is not and int), the scheduler fails. Here is a selection from the stack trace:
ERROR [Scheduler] Could not load or create constructor argument
java.lang.NumberFormatException: For input string: "hundred"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
at java.lang.Integer.parseInt(Integer.java:447)
at java.lang.Integer.<init>(Integer.java:620)
Be sure to correct the error; remember to select the Apply Changes button to commit the changes.
Using other Core JBoss Services
This section of the blog provides some simple code for using other JBoss Core Services; JBossMQ, and Java Mail.
JBossMQ Server (JMS)
The org.jboss.mq.server.jmx.Queue is used to define a JMS Queue Destination on the JBossMQ server. The “name” attribute of the JMX object name of this MBean is used to determine the destination name.
Add the MBean definition to the jboss-service.xml:
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=BlogExampleQueue">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean>
The following code example shows how to use the BlogExampleQueue:
public static void sendMessage(String inMessage) throws NamingException, JMSException {
_log.info("sendMessage().");
String msgText;
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
properties.put(Context.PROVIDER_URL, "jnp://localhost:1099");
properties.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
properties.put("jnp.disableDiscovery", "true");
InitialContext ctx = new InitialContext(properties);
Queue queue = (Queue) ctx.lookup("queue/BlogExampleQueue");
QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup("QueueConnectionFactory");
QueueConnection qc = qcf.createQueueConnection();
try {
QueueSession qs = qc.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender sender = qs.createSender(queue);
TextMessage message = qs.createTextMessage(inMessage);
sender.send(message);
qc.start();
} finally {
qc.close();
}
}
JBOSS Mail Server
JBoss has a built-in implementation of the JavaMail API.
Add the following MBean definition to the jboss-service.xml:
<mbean code="blog.example.services.BlogExampleService"
name="blog.example:service=BlogExampleService"> <depends>jboss:service=Mail</depends>
</mbean>
The following code example shows how to use the JBoss Mail Service. The perform method is called by the JBoss Scheduler. The perform method calls the processInbox method to read inbox messages from a pop3 mail server.
// ServiceBean callback method
public void perform(Date now, long repetitions) {
_log.info("BlogExampleService ============= perform");
this.processInbox();
}
// working method to process messages from the INBOX
public void processInbox() {
_log.info("BlogExampleService ======== processInbox ");
Message[] messages = null;
String[]emaildata = null;
Folder inboxFolder = null;
Store store; // email store
try {
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
properties.put(Context.PROVIDER_URL, "jnp://localhost:1099");
properties.put("java.naming.factory.url.pkgs",
"org.jboss.naming:org.jnp.interfaces");
properties.put("jnp.disableDiscovery", "true");
ctx = new InitialContext(properties);
// JBoss Email Service
ctx = (Context) ctx.lookup("java:");
mailSession = (Session) ctx.lookup("Mail");
// -- Get hold of a POP3 message store, and connect to it --
if (null != mailSession) {
store = mailSession.getStore("pop3");
store.connect(null, "", "");
// -- Try to get hold of the default folder --
inboxFolder = store.getDefaultFolder();
// -- ...and its INBOX --
inboxFolder = inboxFolder.getFolder("INBOX");
// -- Open the folder for read write --
inboxFolder.open(Folder.READ_WRITE);
// retrieve all the messages from the inbox
messages = inboxFolder.getMessages();
for (int x= 0; x< messages.length; x++) {
// delete the email from the inbox
messages[x].setFlag(Flags.Flag.DELETED, true);
}
// create the array from the email message.
emaildata = processMessage(messages[x]);
}
inboxFolder.close(true);
store.close();
} else {
_log.warn("No Mail Session");
}
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (MessagingException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}
private String[] processMessage(Message message) {
String[] stringMessages = new String[100];
int index = 0;
try {
// -- Get the message part (i.e. the message itself) --
Part messagePart = message;
Object content = messagePart.getContent();
// -- or its first body part if it is a multipart message --
if (content instanceof Multipart) {
messagePart = ((Multipart) content).getBodyPart(0);
}
// -- Get the content type --
String contentType = messagePart.getContentType();
if (contentType.startsWith("text/plain") ||
contentType.startsWith("text/html")) {
InputStream is = messagePart.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String thisLine = reader.readLine();
while (thisLine != null) {
thisLine = reader.readLine();
stringMessages[index++] = thisLine;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return stringMessages;
}
The blog entries detailed writing JBoss Custom Service services and using some JBoss core services including; the Scheduler, JBossMQ (JMS), and Mail services. All JBoss services are accessible via the JBoss JMX-Console. The JBoss Services expose lifecycle methods and attributes which can be modified or executed from the console. A major benefit of using a JBoss Service is that you can stop, reconfigure, and restart the service without restarting the JBoss Application Server. Please refer to the JBoss website for more details.
Comments
You've wrote an really good and helpful article! Thanks a lot!
I am a newbie to JBoss and have implemented a Schedular service in JBoss. I have few questions in this regards. My service do have method of perform and I am creating an object in it.
1. If somehow, the created object is killed then how would I know if that object is killed or not. My objective to know about this answer is, if the service or object is killed, JBoss may start it again.
2. Is there a way that I can start multiple copies of a scheduler.
Thanks,
Shahzad Masud
Great Articles. Lately, I've been having a lot of trouble finding online resources that show you how to get anything done quickly. I found this to be really helpful.
Radim, I'm using JBoss 4.0.5 with the EJB3 profile and the scheduler plugin is there.
Alexander, overriding the extra methods using Eclipse doesn't actually break anything. At least the version of Eclipse that I'm using... It automatically created super method calls to pass everything through to the super class. I do appreciate the extra info though.
Anyway, thanks again.
Hi John,
we are using Charts API for creating charts in our application..
Its creating some temp image files for that.. To delete those files, we have put a java.util.Timer (scheduler) object to delete the files from that directory at the scheduled interval
In this deletion code, we have put a check for the tmp/deploy directory check also..
so that whenever the war file is undeployed and that directory is deleted from the temp/deploy folder.. cancel the scheduler as with the new deployment, the new scheduler will be created..
This cancel of the scheduler on undeploy was working perfect fine with JBoss 4.0.3
But now with JBoss 4.0.5, when we are trying to cancel the task, its saying the reference of the timer object is already null.. but still the scheduler task is runing in JBoss.
at the start init of the timer, we are getting the exact path of the tmp/deploy directory for our war file..
and with each scheduler run() method call, we are checking if that directort exist..
if it exist, check the impage dir and try deleting the files from their..
if the directory don't exist(meant war file undeployed), cancel the scheduler task..
Now somehow, when we are running the task, the JBoss is taking control of the timer instance and making our application specific object null.. so we are not able to cancel the scheduler when the war file is undeployed..
Due to that, if I undeploy/deploy the war file 3 times, I will be having the 3 schedule tasks running... 2 will be from previous deploy
So until the server is restarted, the previous 2 will not be killed..
Can you please help me solve this issue...
Thanks John for sharing your experiences through this blog column.
I have been able to implment a jboss scheduler using the schedulable implementation. But, have you come across any example of 'org.jboss.varia.scheduler.ScheduleManager'? I tried to search for examples on the internet as well as Jboss forums, but nothing has come out as of now.
Please let me know in case you have implemented one.
Thanks,
Murali
John, thank you for an excelent article. Just to prevent confusion I'd like to point that default installer for JBoss AS 4.0.4 with EJB3 doesn't contain some of the plugins (for example mentioned scheduler-plugin.jar).
More information at http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3958764
Cheers
Radim
Thank you, John, this is an excellent primer, it helped me to find my way in creating a custom service. However I would point out that you are overriding all the methods (as suggested by Eclipse) which actually breaks the functionality put into the ServiceMBeanSupport class.
As they say at JBoss Wiki, we only need to override four methods:
And, yes, getName() should also be overriden.
If we do so, the startService() gets invoked by the server at the appropriate time. Otherwise, it doesn't get invoked.
Thank you again for your valuable help.
Post new comment