/*
* Copyright 2001-2009 Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
*/
package org.quartz.ee.jmx.jboss;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import org.quartz.Scheduler;
import org.quartz.SchedulerConfigException;
import org.quartz.SchedulerException;
import org.quartz.impl.StdSchedulerFactory;
import org.jboss.naming.NonSerializableFactory;
import org.jboss.system.ServiceMBeanSupport;
/**
* JBoss specific MBean implementation for configuring, starting, and
* binding to JNDI a Quartz Scheduler instance.
*
* <p>
* Sample MBean deployment descriptor:
* <a href="doc-files/quartz-service.xml" type="text/plain">quartz-service.xml</a>
* </p>
*
* <p>
* <b>Note:</b> The Scheduler instance bound to JNDI is not Serializable, so
* you will get a null reference back if you try to retrieve it from outside
* the JBoss server in which it was bound. If you have a need for remote
* access to a Scheduler instance you may want to consider using Quartz's RMI
* support instead.
* </p>
*
* @see org.quartz.ee.jmx.jboss.QuartzServiceMBean
*
* @author Andrew Collins
*/
public class QuartzService extends ServiceMBeanSupport implements
QuartzServiceMBean {
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Data members.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
private Properties properties;
private StdSchedulerFactory schedulerFactory;
private String jndiName;
private String propertiesFile;
private boolean error;
private boolean useProperties;
private boolean usePropertiesFile;
/*
* If true, the scheduler will be started. If false, the scheduler is initailized
* (and available) but start() is not called - it will not execute jobs.
*/
private boolean startScheduler = true;
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Constructors.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public QuartzService() {
// flag initialization errors
error = false;
// use PropertiesFile attribute
usePropertiesFile = false;
propertiesFile = "";
// use Properties attribute
useProperties = false;
properties = new Properties();
// default JNDI name for Scheduler
jndiName = "Quartz";
}
/*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Interface.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public void setJndiName(String jndiName) throws Exception {
String oldName = this.jndiName;
this.jndiName = jndiName;
if (super.getState() == STARTED) {
unbind(oldName);
try {
rebind();
} catch (NamingException ne) {
log.error("Failed to rebind Scheduler", ne);
throw new SchedulerConfigException(
"Failed to rebind Scheduler - ", ne);
}
}
}
public String getJndiName() {
return jndiName;
}
@Override
public String getName() {
return "QuartzService(" + jndiName + ")";
}
public void setProperties(String properties) {
if (usePropertiesFile) {
log
.error("Must specify only one of 'Properties' or 'PropertiesFile'");
error = true;
return;
}
useProperties = true;
try {
properties = properties.replace(File.separator, "/");
ByteArrayInputStream bais = new ByteArrayInputStream(properties
.getBytes());
this.properties = new Properties();
this.properties.load(bais);
} catch (IOException ioe) {
// should not happen
}
}
public String getProperties() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
properties.store(baos, "");
return new String(baos.toByteArray());
} catch (IOException ioe) {
// should not happen
return "";
}
}
public void setPropertiesFile(String propertiesFile) {
if (useProperties) {
log
.error("Must specify only one of 'Properties' or 'PropertiesFile'");
error = true;
return;
}
usePropertiesFile = true;
this.propertiesFile = propertiesFile;
}
public String getPropertiesFile() {
return propertiesFile;
}
public void setStartScheduler(boolean startScheduler) {
this.startScheduler = startScheduler;
}
public boolean getStartScheduler() {
return startScheduler;
}
@Override
public void createService() throws Exception {
log.info("Create QuartzService(" + jndiName + ")...");
if (error) {
log
.error("Must specify only one of 'Properties' or 'PropertiesFile'");
throw new Exception(
"Must specify only one of 'Properties' or 'PropertiesFile'");
}
schedulerFactory = new StdSchedulerFactory();
try {
if (useProperties) {
schedulerFactory.initialize(properties);
}
if (usePropertiesFile) {
schedulerFactory.initialize(propertiesFile);
}
} catch (Exception e) {
log.error("Failed to initialize Scheduler", e);
throw new SchedulerConfigException(
"Failed to initialize Scheduler - ", e);
}
log.info("QuartzService(" + jndiName + ") created.");
}
@Override
public void destroyService() throws Exception {
log.info("Destroy QuartzService(" + jndiName + ")...");
schedulerFactory = null;
log.info("QuartzService(" + jndiName + ") destroyed.");
}
@Override
public void startService() throws Exception {
log.info("Start QuartzService(" + jndiName + ")...");
try {
rebind();
} catch (NamingException ne) {
log.error("Failed to rebind Scheduler", ne);
throw new SchedulerConfigException("Failed to rebind Scheduler - ",
ne);
}
try {
Scheduler scheduler = schedulerFactory.getScheduler();
if (startScheduler) {
scheduler.start();
} else {
log.info("Skipping starting the scheduler (will not run jobs).");
}
} catch (Exception e) {
log.error("Failed to start Scheduler", e);
throw new SchedulerConfigException("Failed to start Scheduler - ",
e);
}
log.info("QuartzService(" + jndiName + ") started.");
}
@Override
public void stopService() throws Exception {
log.info("Stop QuartzService(" + jndiName + ")...");
try {
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.shutdown();
} catch (Exception e) {
log.error("Failed to shutdown Scheduler", e);
throw new SchedulerConfigException(
"Failed to shutdown Scheduler - ", e);
}
unbind(jndiName);
log.info("QuartzService(" + jndiName + ") stopped.");
}
private void rebind() throws NamingException, SchedulerException {
InitialContext rootCtx = null;
try {
rootCtx = new InitialContext();
Name fullName = rootCtx.getNameParser("").parse(jndiName);
Scheduler scheduler = schedulerFactory.getScheduler();
NonSerializableFactory.rebind(fullName, scheduler, true);
} finally {
if (rootCtx != null) {
try {
rootCtx.close();
} catch (NamingException ignore) {}
}
}
}
private void unbind(String name) {
InitialContext rootCtx = null;
try {
rootCtx = new InitialContext();
rootCtx.unbind(name);
NonSerializableFactory.unbind(name);
} catch (NamingException e) {
log.warn("Failed to unbind scheduler with jndiName: " + name, e);
} finally {
if (rootCtx != null) {
try {
rootCtx.close();
} catch (NamingException ignore) {}
}
}
}
}