Rename modules {org.springframework.*=>spring-*}
This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.
Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example
$ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history up until the renaming event, where
$ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history for all changes to the file, before and after the
renaming.
See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
/**
|
||||
* Exception thrown on failed authentication.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class MailAuthenticationException extends MailException {
|
||||
|
||||
/**
|
||||
* Constructor for MailAuthenticationException.
|
||||
* @param msg message
|
||||
*/
|
||||
public MailAuthenticationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailAuthenticationException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailAuthenticationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailAuthenticationException.
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailAuthenticationException(Throwable cause) {
|
||||
super("Authentication failed", cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
|
||||
/**
|
||||
* Base class for all mail exceptions.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
*/
|
||||
public abstract class MailException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor for MailException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* This is a common interface for mail messages, allowing a user to set key
|
||||
* values required in assembling a mail message, without needing to know if
|
||||
* the underlying message is a simple text message or a more sophisticated
|
||||
* MIME message.
|
||||
*
|
||||
* <p>Implemented by both SimpleMailMessage and MimeMessageHelper,
|
||||
* to let message population code interact with a simple message or a
|
||||
* MIME message through a common interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see SimpleMailMessage
|
||||
* @see org.springframework.mail.javamail.MimeMessageHelper
|
||||
*/
|
||||
public interface MailMessage {
|
||||
|
||||
public void setFrom(String from) throws MailParseException;
|
||||
|
||||
public void setReplyTo(String replyTo) throws MailParseException;
|
||||
|
||||
public void setTo(String to) throws MailParseException;
|
||||
|
||||
public void setTo(String[] to) throws MailParseException;
|
||||
|
||||
public void setCc(String cc) throws MailParseException;
|
||||
|
||||
public void setCc(String[] cc) throws MailParseException;
|
||||
|
||||
public void setBcc(String bcc) throws MailParseException;
|
||||
|
||||
public void setBcc(String[] bcc) throws MailParseException;
|
||||
|
||||
public void setSentDate(Date sentDate) throws MailParseException;
|
||||
|
||||
public void setSubject(String subject) throws MailParseException;
|
||||
|
||||
public void setText(String text) throws MailParseException;
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
/**
|
||||
* Exception thrown if illegal message properties are encountered.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class MailParseException extends MailException {
|
||||
|
||||
/**
|
||||
* Constructor for MailParseException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailParseException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailParseException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailParseException.
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailParseException(Throwable cause) {
|
||||
super("Could not parse mail", cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
/**
|
||||
* Exception to be thrown by user code if a mail cannot be prepared properly,
|
||||
* for example when a Velocity template cannot be rendered for the mail text.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see org.springframework.ui.velocity.VelocityEngineUtils#mergeTemplateIntoString
|
||||
* @see org.springframework.ui.freemarker.FreeMarkerTemplateUtils#processTemplateIntoString
|
||||
*/
|
||||
public class MailPreparationException extends MailException {
|
||||
|
||||
/**
|
||||
* Constructor for MailPreparationException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailPreparationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailPreparationException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailPreparationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
public MailPreparationException(Throwable cause) {
|
||||
super("Could not prepare mail", cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Exception thrown when a mail sending error is encountered.
|
||||
* Can register failed messages with their exceptions.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class MailSendException extends MailException {
|
||||
|
||||
private transient final Map<Object, Exception> failedMessages;
|
||||
|
||||
private Exception[] messageExceptions;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for MailSendException.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public MailSendException(String msg) {
|
||||
this(msg, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for MailSendException.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
*/
|
||||
public MailSendException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.failedMessages = new LinkedHashMap<Object, Exception>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for registration of failed messages, with the
|
||||
* messages that failed as keys, and the thrown exceptions as values.
|
||||
* <p>The messages should be the same that were originally passed
|
||||
* to the invoked send method.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause from the mail API in use
|
||||
* @param failedMessages Map of failed messages as keys and thrown
|
||||
* exceptions as values
|
||||
*/
|
||||
public MailSendException(String msg, Throwable cause, Map<Object, Exception> failedMessages) {
|
||||
super(msg, cause);
|
||||
this.failedMessages = new LinkedHashMap<Object, Exception>(failedMessages);
|
||||
this.messageExceptions = failedMessages.values().toArray(new Exception[failedMessages.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for registration of failed messages, with the
|
||||
* messages that failed as keys, and the thrown exceptions as values.
|
||||
* <p>The messages should be the same that were originally passed
|
||||
* to the invoked send method.
|
||||
* @param failedMessages Map of failed messages as keys and thrown
|
||||
* exceptions as values
|
||||
*/
|
||||
public MailSendException(Map<Object, Exception> failedMessages) {
|
||||
this(null, null, failedMessages);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a Map with the failed messages as keys, and the thrown exceptions
|
||||
* as values.
|
||||
* <p>Note that a general mail server connection failure will not result
|
||||
* in failed messages being returned here: A message will only be
|
||||
* contained here if actually sending it was attempted but failed.
|
||||
* <p>The messages will be the same that were originally passed to the
|
||||
* invoked send method, that is, SimpleMailMessages in case of using
|
||||
* the generic MailSender interface.
|
||||
* <p>In case of sending MimeMessage instances via JavaMailSender,
|
||||
* the messages will be of type MimeMessage.
|
||||
* <p><b>NOTE:</b> This Map will not be available after serialization.
|
||||
* Use {@link #getMessageExceptions()} in such a scenario, which will
|
||||
* be available after serialization as well.
|
||||
* @return the Map of failed messages as keys and thrown exceptions as values
|
||||
* @see SimpleMailMessage
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
*/
|
||||
public final Map<Object, Exception> getFailedMessages() {
|
||||
return this.failedMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array with thrown message exceptions.
|
||||
* <p>Note that a general mail server connection failure will not result
|
||||
* in failed messages being returned here: A message will only be
|
||||
* contained here if actually sending it was attempted but failed.
|
||||
* @return the array of thrown message exceptions,
|
||||
* or an empty array if no failed messages
|
||||
*/
|
||||
public final Exception[] getMessageExceptions() {
|
||||
return (this.messageExceptions != null ? this.messageExceptions : new Exception[0]);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
return super.getMessage();
|
||||
}
|
||||
else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String baseMessage = super.getMessage();
|
||||
if (baseMessage != null) {
|
||||
sb.append(baseMessage).append(". ");
|
||||
}
|
||||
sb.append("Failed messages: ");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
sb.append(subEx.toString());
|
||||
if (i < this.messageExceptions.length - 1) {
|
||||
sb.append("; ");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
return super.toString();
|
||||
}
|
||||
else {
|
||||
StringBuilder sb = new StringBuilder(super.toString());
|
||||
sb.append("; message exceptions (").append(this.messageExceptions.length).append(") are:");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
sb.append('\n').append("Failed message ").append(i + 1).append(": ");
|
||||
sb.append(subEx);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintStream ps) {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
super.printStackTrace(ps);
|
||||
}
|
||||
else {
|
||||
ps.println(super.toString() + "; message exception details (" +
|
||||
this.messageExceptions.length + ") are:");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
ps.println("Failed message " + (i + 1) + ":");
|
||||
subEx.printStackTrace(ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintWriter pw) {
|
||||
if (ObjectUtils.isEmpty(this.messageExceptions)) {
|
||||
super.printStackTrace(pw);
|
||||
}
|
||||
else {
|
||||
pw.println(super.toString() + "; message exception details (" +
|
||||
this.messageExceptions.length + ") are:");
|
||||
for (int i = 0; i < this.messageExceptions.length; i++) {
|
||||
Exception subEx = this.messageExceptions[i];
|
||||
pw.println("Failed message " + (i + 1) + ":");
|
||||
subEx.printStackTrace(pw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
/**
|
||||
* This interface defines a strategy for sending simple mails. Can be
|
||||
* implemented for a variety of mailing systems due to the simple requirements.
|
||||
* For richer functionality like MIME messages, consider JavaMailSender.
|
||||
*
|
||||
* <p>Allows for easy testing of clients, as it does not depend on JavaMail's
|
||||
* infrastructure classes: no mocking of JavaMail Session or Transport necessary.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 10.09.2003
|
||||
* @see org.springframework.mail.javamail.JavaMailSender
|
||||
*/
|
||||
public interface MailSender {
|
||||
|
||||
/**
|
||||
* Send the given simple mail message.
|
||||
* @param simpleMessage the message to send
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing the message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending the message
|
||||
*/
|
||||
void send(SimpleMailMessage simpleMessage) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the given array of simple mail messages in batch.
|
||||
* @param simpleMessages the messages to send
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing a message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
*/
|
||||
void send(SimpleMailMessage[] simpleMessages) throws MailException;
|
||||
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Models a simple mail message, including data such as the from, to, cc, subject, and text fields.
|
||||
*
|
||||
* <p>Consider <code>JavaMailSender</code> and JavaMail <code>MimeMessages</code> for creating
|
||||
* more sophisticated messages, for example messages with attachments, special
|
||||
* character encodings, or personal names that accompany mail addresses.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 10.09.2003
|
||||
* @see MailSender
|
||||
* @see org.springframework.mail.javamail.JavaMailSender
|
||||
* @see org.springframework.mail.javamail.MimeMessagePreparator
|
||||
* @see org.springframework.mail.javamail.MimeMessageHelper
|
||||
* @see org.springframework.mail.javamail.MimeMailMessage
|
||||
*/
|
||||
public class SimpleMailMessage implements MailMessage, Serializable {
|
||||
|
||||
private String from;
|
||||
|
||||
private String replyTo;
|
||||
|
||||
private String[] to;
|
||||
|
||||
private String[] cc;
|
||||
|
||||
private String[] bcc;
|
||||
|
||||
private Date sentDate;
|
||||
|
||||
private String subject;
|
||||
|
||||
private String text;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new <code>SimpleMailMessage</code>.
|
||||
*/
|
||||
public SimpleMailMessage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor for creating a new <code>SimpleMailMessage</code> from the state
|
||||
* of an existing <code>SimpleMailMessage</code> instance.
|
||||
* @throws IllegalArgumentException if the supplied message is <code>null</code>
|
||||
*/
|
||||
public SimpleMailMessage(SimpleMailMessage original) {
|
||||
Assert.notNull(original, "The 'original' message argument cannot be null");
|
||||
this.from = original.getFrom();
|
||||
this.replyTo = original.getReplyTo();
|
||||
if (original.getTo() != null) {
|
||||
this.to = copy(original.getTo());
|
||||
}
|
||||
if (original.getCc() != null) {
|
||||
this.cc = copy(original.getCc());
|
||||
}
|
||||
if (original.getBcc() != null) {
|
||||
this.bcc = copy(original.getBcc());
|
||||
}
|
||||
this.sentDate = original.getSentDate();
|
||||
this.subject = original.getSubject();
|
||||
this.text = original.getText();
|
||||
}
|
||||
|
||||
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
public String getFrom() {
|
||||
return this.from;
|
||||
}
|
||||
|
||||
public void setReplyTo(String replyTo) {
|
||||
this.replyTo = replyTo;
|
||||
}
|
||||
|
||||
public String getReplyTo() {
|
||||
return replyTo;
|
||||
}
|
||||
|
||||
public void setTo(String to) {
|
||||
this.to = new String[] {to};
|
||||
}
|
||||
|
||||
public void setTo(String[] to) {
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public String[] getTo() {
|
||||
return this.to;
|
||||
}
|
||||
|
||||
public void setCc(String cc) {
|
||||
this.cc = new String[] {cc};
|
||||
}
|
||||
|
||||
public void setCc(String[] cc) {
|
||||
this.cc = cc;
|
||||
}
|
||||
|
||||
public String[] getCc() {
|
||||
return cc;
|
||||
}
|
||||
|
||||
public void setBcc(String bcc) {
|
||||
this.bcc = new String[] {bcc};
|
||||
}
|
||||
|
||||
public void setBcc(String[] bcc) {
|
||||
this.bcc = bcc;
|
||||
}
|
||||
|
||||
public String[] getBcc() {
|
||||
return bcc;
|
||||
}
|
||||
|
||||
public void setSentDate(Date sentDate) {
|
||||
this.sentDate = sentDate;
|
||||
}
|
||||
|
||||
public Date getSentDate() {
|
||||
return sentDate;
|
||||
}
|
||||
|
||||
public void setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copy the contents of this message to the given target message.
|
||||
* @param target the <code>MailMessage</code> to copy to
|
||||
* @throws IllegalArgumentException if the supplied <code>target</code> is <code>null</code>
|
||||
*/
|
||||
public void copyTo(MailMessage target) {
|
||||
Assert.notNull(target, "The 'target' message argument cannot be null");
|
||||
if (getFrom() != null) {
|
||||
target.setFrom(getFrom());
|
||||
}
|
||||
if (getReplyTo() != null) {
|
||||
target.setReplyTo(getReplyTo());
|
||||
}
|
||||
if (getTo() != null) {
|
||||
target.setTo(getTo());
|
||||
}
|
||||
if (getCc() != null) {
|
||||
target.setCc(getCc());
|
||||
}
|
||||
if (getBcc() != null) {
|
||||
target.setBcc(getBcc());
|
||||
}
|
||||
if (getSentDate() != null) {
|
||||
target.setSentDate(getSentDate());
|
||||
}
|
||||
if (getSubject() != null) {
|
||||
target.setSubject(getSubject());
|
||||
}
|
||||
if (getText() != null) {
|
||||
target.setText(getText());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("SimpleMailMessage: ");
|
||||
sb.append("from=").append(this.from).append("; ");
|
||||
sb.append("replyTo=").append(this.replyTo).append("; ");
|
||||
sb.append("to=").append(StringUtils.arrayToCommaDelimitedString(this.to)).append("; ");
|
||||
sb.append("cc=").append(StringUtils.arrayToCommaDelimitedString(this.cc)).append("; ");
|
||||
sb.append("bcc=").append(StringUtils.arrayToCommaDelimitedString(this.bcc)).append("; ");
|
||||
sb.append("sentDate=").append(this.sentDate).append("; ");
|
||||
sb.append("subject=").append(this.subject).append("; ");
|
||||
sb.append("text=").append(this.text);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof SimpleMailMessage)) {
|
||||
return false;
|
||||
}
|
||||
SimpleMailMessage otherMessage = (SimpleMailMessage) other;
|
||||
return (ObjectUtils.nullSafeEquals(this.from, otherMessage.from) &&
|
||||
ObjectUtils.nullSafeEquals(this.replyTo, otherMessage.replyTo) &&
|
||||
java.util.Arrays.equals(this.to, otherMessage.to) &&
|
||||
java.util.Arrays.equals(this.cc, otherMessage.cc) &&
|
||||
java.util.Arrays.equals(this.bcc, otherMessage.bcc) &&
|
||||
ObjectUtils.nullSafeEquals(this.sentDate, otherMessage.sentDate) &&
|
||||
ObjectUtils.nullSafeEquals(this.subject, otherMessage.subject) &&
|
||||
ObjectUtils.nullSafeEquals(this.text, otherMessage.text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = (this.from == null ? 0 : this.from.hashCode());
|
||||
hashCode = 29 * hashCode + (this.replyTo == null ? 0 : this.replyTo.hashCode());
|
||||
for (int i = 0; this.to != null && i < this.to.length; i++) {
|
||||
hashCode = 29 * hashCode + (this.to == null ? 0 : this.to[i].hashCode());
|
||||
}
|
||||
for (int i = 0; this.cc != null && i < this.cc.length; i++) {
|
||||
hashCode = 29 * hashCode + (this.cc == null ? 0 : this.cc[i].hashCode());
|
||||
}
|
||||
for (int i = 0; this.bcc != null && i < this.bcc.length; i++) {
|
||||
hashCode = 29 * hashCode + (this.bcc == null ? 0 : this.bcc[i].hashCode());
|
||||
}
|
||||
hashCode = 29 * hashCode + (this.sentDate == null ? 0 : this.sentDate.hashCode());
|
||||
hashCode = 29 * hashCode + (this.subject == null ? 0 : this.subject.hashCode());
|
||||
hashCode = 29 * hashCode + (this.text == null ? 0 : this.text.hashCode());
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
|
||||
private static String[] copy(String[] state) {
|
||||
String[] copy = new String[state.length];
|
||||
System.arraycopy(state, 0, copy, 0, state.length);
|
||||
return copy;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Spring-configurable <code>FileTypeMap</code> implementation that will read
|
||||
* MIME type to file extension mappings from a standard JavaMail MIME type
|
||||
* mapping file, using a standard <code>MimetypesFileTypeMap</code> underneath.
|
||||
*
|
||||
* <p>The mapping file should be in the following format, as specified by the
|
||||
* Java Activation Framework:
|
||||
*
|
||||
* <pre>
|
||||
* # map text/html to .htm and .html files
|
||||
* text/html html htm HTML HTM</pre>
|
||||
*
|
||||
* Lines starting with <code>#</code> are treated as comments and are ignored. All
|
||||
* other lines are treated as mappings. Each mapping line should contain the MIME
|
||||
* type as the first entry and then each file extension to map to that MIME type
|
||||
* as subsequent entries. Each entry is separated by spaces or tabs.
|
||||
*
|
||||
* <p>By default, the mappings in the <code>mime.types</code> file located in the
|
||||
* same package as this class are used, which cover many common file extensions
|
||||
* (in contrast to the out-of-the-box mappings in <code>activation.jar</code>).
|
||||
* This can be overridden using the <code>mappingLocation</code> property.
|
||||
*
|
||||
* <p>Additional mappings can be added via the <code>mappings</code> bean property,
|
||||
* as lines that follow the <code>mime.types<code> file format.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see #setMappingLocation
|
||||
* @see #setMappings
|
||||
* @see javax.activation.MimetypesFileTypeMap
|
||||
*/
|
||||
public class ConfigurableMimeFileTypeMap extends FileTypeMap implements InitializingBean {
|
||||
|
||||
/**
|
||||
* The <code>Resource</code> to load the mapping file from.
|
||||
*/
|
||||
private Resource mappingLocation = new ClassPathResource("mime.types", getClass());
|
||||
|
||||
/**
|
||||
* Used to configure additional mappings.
|
||||
*/
|
||||
private String[] mappings;
|
||||
|
||||
/**
|
||||
* The delegate FileTypeMap, compiled from the mappings in the mapping file
|
||||
* and the entries in the <code>mappings</code> property.
|
||||
*/
|
||||
private FileTypeMap fileTypeMap;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the <code>Resource</code> from which mappings are loaded.
|
||||
* <p>Needs to follow the <code>mime.types<code> file format, as specified
|
||||
* by the Java Activation Framework, containing lines such as:<br>
|
||||
* <code>text/html html htm HTML HTM</code>
|
||||
*/
|
||||
public void setMappingLocation(Resource mappingLocation) {
|
||||
this.mappingLocation = mappingLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify additional MIME type mappings as lines that follow the
|
||||
* <code>mime.types<code> file format, as specified by the
|
||||
* Java Activation Framework, for example:<br>
|
||||
* <code>text/html html htm HTML HTM</code>
|
||||
*/
|
||||
public void setMappings(String[] mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the final merged mapping set.
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
getFileTypeMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the delegate FileTypeMap, compiled from the mappings in the mapping file
|
||||
* and the entries in the <code>mappings</code> property.
|
||||
* @see #setMappingLocation
|
||||
* @see #setMappings
|
||||
* @see #createFileTypeMap
|
||||
*/
|
||||
protected final FileTypeMap getFileTypeMap() {
|
||||
if (this.fileTypeMap == null) {
|
||||
try {
|
||||
this.fileTypeMap = createFileTypeMap(this.mappingLocation, this.mappings);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not load specified MIME type mapping file: " + this.mappingLocation, ex);
|
||||
}
|
||||
}
|
||||
return this.fileTypeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a {@link FileTypeMap} from the mappings in the given mapping file
|
||||
* and the given mapping entries.
|
||||
* <p>The default implementation creates an Activation Framework {@link MimetypesFileTypeMap},
|
||||
* passing in an InputStream from the mapping resource (if any) and registering
|
||||
* the mapping lines programmatically.
|
||||
* @param mappingLocation a <code>mime.types</code> mapping resource (can be <code>null</code>)
|
||||
* @param mappings MIME type mapping lines (can be <code>null</code>)
|
||||
* @return the compiled FileTypeMap
|
||||
* @throws IOException if resource access failed
|
||||
* @see javax.activation.MimetypesFileTypeMap#MimetypesFileTypeMap(java.io.InputStream)
|
||||
* @see javax.activation.MimetypesFileTypeMap#addMimeTypes(String)
|
||||
*/
|
||||
protected FileTypeMap createFileTypeMap(Resource mappingLocation, String[] mappings) throws IOException {
|
||||
MimetypesFileTypeMap fileTypeMap = null;
|
||||
if (mappingLocation != null) {
|
||||
InputStream is = mappingLocation.getInputStream();
|
||||
try {
|
||||
fileTypeMap = new MimetypesFileTypeMap(is);
|
||||
}
|
||||
finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
fileTypeMap = new MimetypesFileTypeMap();
|
||||
}
|
||||
if (mappings != null) {
|
||||
for (String mapping : mappings) {
|
||||
fileTypeMap.addMimeTypes(mapping);
|
||||
}
|
||||
}
|
||||
return fileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to the underlying FileTypeMap.
|
||||
* @see #getFileTypeMap()
|
||||
*/
|
||||
@Override
|
||||
public String getContentType(File file) {
|
||||
return getFileTypeMap().getContentType(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to the underlying FileTypeMap.
|
||||
* @see #getFileTypeMap()
|
||||
*/
|
||||
@Override
|
||||
public String getContentType(String fileName) {
|
||||
return getFileTypeMap().getContentType(fileName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Editor for <code>java.mail.internet.InternetAddress</code>,
|
||||
* to directly populate an InternetAddress property.
|
||||
*
|
||||
* <p>Expects the same syntax as InternetAddress's constructor with
|
||||
* a String argument. Converts empty Strings into null values.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2.3
|
||||
* @see javax.mail.internet.InternetAddress
|
||||
*/
|
||||
public class InternetAddressEditor extends PropertyEditorSupport {
|
||||
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
if (StringUtils.hasText(text)) {
|
||||
try {
|
||||
setValue(new InternetAddress(text));
|
||||
}
|
||||
catch (AddressException ex) {
|
||||
throw new IllegalArgumentException("Could not parse mail address: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsText() {
|
||||
InternetAddress value = (InternetAddress) getValue();
|
||||
return (value != null ? value.toUnicodeString() : "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.MailSender;
|
||||
|
||||
/**
|
||||
* Extended {@link org.springframework.mail.MailSender} interface for JavaMail,
|
||||
* supporting MIME messages both as direct arguments and through preparation
|
||||
* callbacks. Typically used in conjunction with the {@link MimeMessageHelper}
|
||||
* class for convenient creation of JavaMail {@link MimeMessage MimeMessages},
|
||||
* including attachments etc.
|
||||
*
|
||||
* <p>Clients should talk to the mail sender through this interface if they need
|
||||
* mail functionality beyond {@link org.springframework.mail.SimpleMailMessage}.
|
||||
* The production implementation is {@link JavaMailSenderImpl}; for testing,
|
||||
* mocks can be created based on this interface. Clients will typically receive
|
||||
* the JavaMailSender reference through dependency injection.
|
||||
*
|
||||
* <p>The recommended way of using this interface is the {@link MimeMessagePreparator}
|
||||
* mechanism, possibly using a {@link MimeMessageHelper} for populating the message.
|
||||
* See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
|
||||
*
|
||||
* <p>The entire JavaMail {@link javax.mail.Session} management is abstracted
|
||||
* by the JavaMailSender. Client code should not deal with a Session in any way,
|
||||
* rather leave the entire JavaMail configuration and resource handling to the
|
||||
* JavaMailSender implementation. This also increases testability.
|
||||
*
|
||||
* <p>A JavaMailSender client is not as easy to test as a plain
|
||||
* {@link org.springframework.mail.MailSender} client, but still straightforward
|
||||
* compared to traditional JavaMail code: Just let {@link #createMimeMessage()}
|
||||
* return a plain {@link MimeMessage} created with a
|
||||
* <code>Session.getInstance(new Properties())</code> call, and check the passed-in
|
||||
* messages in your mock implementations of the various <code>send</code> methods.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.10.2003
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see javax.mail.Session
|
||||
* @see JavaMailSenderImpl
|
||||
* @see MimeMessagePreparator
|
||||
* @see MimeMessageHelper
|
||||
*/
|
||||
public interface JavaMailSender extends MailSender {
|
||||
|
||||
/**
|
||||
* Create a new JavaMail MimeMessage for the underlying JavaMail Session
|
||||
* of this sender. Needs to be called to create MimeMessage instances
|
||||
* that can be prepared by the client and passed to send(MimeMessage).
|
||||
* @return the new MimeMessage instance
|
||||
* @see #send(MimeMessage)
|
||||
* @see #send(MimeMessage[])
|
||||
*/
|
||||
MimeMessage createMimeMessage();
|
||||
|
||||
/**
|
||||
* Create a new JavaMail MimeMessage for the underlying JavaMail Session
|
||||
* of this sender, using the given input stream as the message source.
|
||||
* @param contentStream the raw MIME input stream for the message
|
||||
* @return the new MimeMessage instance
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of message creation failure
|
||||
*/
|
||||
MimeMessage createMimeMessage(InputStream contentStream) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the given JavaMail MIME message.
|
||||
* The message needs to have been created with {@link #createMimeMessage()}.
|
||||
* @param mimeMessage message to send
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending the message
|
||||
* @see #createMimeMessage
|
||||
*/
|
||||
void send(MimeMessage mimeMessage) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the given array of JavaMail MIME messages in batch.
|
||||
* The messages need to have been created with {@link #createMimeMessage()}.
|
||||
* @param mimeMessages messages to send
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
* @see #createMimeMessage
|
||||
*/
|
||||
void send(MimeMessage[] mimeMessages) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the JavaMail MIME message prepared by the given MimeMessagePreparator.
|
||||
* <p>Alternative way to prepare MimeMessage instances, instead of
|
||||
* {@link #createMimeMessage()} and {@link #send(MimeMessage)} calls.
|
||||
* Takes care of proper exception conversion.
|
||||
* @param mimeMessagePreparator the preparator to use
|
||||
* @throws org.springframework.mail.MailPreparationException
|
||||
* in case of failure when preparing the message
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing the message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending the message
|
||||
*/
|
||||
void send(MimeMessagePreparator mimeMessagePreparator) throws MailException;
|
||||
|
||||
/**
|
||||
* Send the JavaMail MIME messages prepared by the given MimeMessagePreparators.
|
||||
* <p>Alternative way to prepare MimeMessage instances, instead of
|
||||
* {@link #createMimeMessage()} and {@link #send(MimeMessage[])} calls.
|
||||
* Takes care of proper exception conversion.
|
||||
* @param mimeMessagePreparators the preparator to use
|
||||
* @throws org.springframework.mail.MailPreparationException
|
||||
* in case of failure when preparing a message
|
||||
* @throws org.springframework.mail.MailParseException
|
||||
* in case of failure when parsing a message
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
*/
|
||||
void send(MimeMessagePreparator[] mimeMessagePreparators) throws MailException;
|
||||
|
||||
}
|
||||
@@ -1,462 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.AuthenticationFailedException;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.NoSuchProviderException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailAuthenticationException;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.MailParseException;
|
||||
import org.springframework.mail.MailPreparationException;
|
||||
import org.springframework.mail.MailSendException;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Production implementation of the {@link JavaMailSender} interface,
|
||||
* supporting both JavaMail {@link MimeMessage MimeMessages} and Spring
|
||||
* {@link SimpleMailMessage SimpleMailMessages}. Can also be used as a
|
||||
* plain {@link org.springframework.mail.MailSender} implementation.
|
||||
*
|
||||
* <p>Allows for defining all settings locally as bean properties.
|
||||
* Alternatively, a pre-configured JavaMail {@link javax.mail.Session} can be
|
||||
* specified, possibly pulled from an application server's JNDI environment.
|
||||
*
|
||||
* <p>Non-default properties in this object will always override the settings
|
||||
* in the JavaMail <code>Session</code>. Note that if overriding all values locally,
|
||||
* there is no added value in setting a pre-configured <code>Session</code>.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 10.09.2003
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
* @see javax.mail.Session
|
||||
* @see #setSession
|
||||
* @see #setJavaMailProperties
|
||||
* @see #setHost
|
||||
* @see #setPort
|
||||
* @see #setUsername
|
||||
* @see #setPassword
|
||||
*/
|
||||
public class JavaMailSenderImpl implements JavaMailSender {
|
||||
|
||||
/** The default protocol: 'smtp' */
|
||||
public static final String DEFAULT_PROTOCOL = "smtp";
|
||||
|
||||
/** The default port: -1 */
|
||||
public static final int DEFAULT_PORT = -1;
|
||||
|
||||
private static final String HEADER_MESSAGE_ID = "Message-ID";
|
||||
|
||||
|
||||
private Properties javaMailProperties = new Properties();
|
||||
|
||||
private Session session;
|
||||
|
||||
private String protocol;
|
||||
|
||||
private String host;
|
||||
|
||||
private int port = DEFAULT_PORT;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String defaultEncoding;
|
||||
|
||||
private FileTypeMap defaultFileTypeMap;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>JavaMailSenderImpl</code> class.
|
||||
* <p>Initializes the {@link #setDefaultFileTypeMap "defaultFileTypeMap"}
|
||||
* property with a default {@link ConfigurableMimeFileTypeMap}.
|
||||
*/
|
||||
public JavaMailSenderImpl() {
|
||||
ConfigurableMimeFileTypeMap fileTypeMap = new ConfigurableMimeFileTypeMap();
|
||||
fileTypeMap.afterPropertiesSet();
|
||||
this.defaultFileTypeMap = fileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set JavaMail properties for the <code>Session</code>.
|
||||
* <p>A new <code>Session</code> will be created with those properties.
|
||||
* Use either this method or {@link #setSession}, but not both.
|
||||
* <p>Non-default properties in this instance will override given
|
||||
* JavaMail properties.
|
||||
*/
|
||||
public void setJavaMailProperties(Properties javaMailProperties) {
|
||||
this.javaMailProperties = javaMailProperties;
|
||||
synchronized (this) {
|
||||
this.session = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow Map access to the JavaMail properties of this sender,
|
||||
* with the option to add or override specific entries.
|
||||
* <p>Useful for specifying entries directly, for example via
|
||||
* "javaMailProperties[mail.smtp.auth]".
|
||||
*/
|
||||
public Properties getJavaMailProperties() {
|
||||
return this.javaMailProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JavaMail <code>Session</code>, possibly pulled from JNDI.
|
||||
* <p>Default is a new <code>Session</code> without defaults, that is
|
||||
* completely configured via this instance's properties.
|
||||
* <p>If using a pre-configured <code>Session</code>, non-default properties
|
||||
* in this instance will override the settings in the <code>Session</code>.
|
||||
* @see #setJavaMailProperties
|
||||
*/
|
||||
public synchronized void setSession(Session session) {
|
||||
Assert.notNull(session, "Session must not be null");
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JavaMail <code>Session</code>,
|
||||
* lazily initializing it if hasn't been specified explicitly.
|
||||
*/
|
||||
public synchronized Session getSession() {
|
||||
if (this.session == null) {
|
||||
this.session = Session.getInstance(this.javaMailProperties);
|
||||
}
|
||||
return this.session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mail protocol. Default is "smtp".
|
||||
*/
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mail protocol.
|
||||
*/
|
||||
public String getProtocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mail server host, typically an SMTP host.
|
||||
* <p>Default is the default host of the underlying JavaMail Session.
|
||||
*/
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mail server host.
|
||||
*/
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mail server port.
|
||||
* <p>Default is {@link #DEFAULT_PORT}, letting JavaMail use the default
|
||||
* SMTP port (25).
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mail server port.
|
||||
*/
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the username for the account at the mail host, if any.
|
||||
* <p>Note that the underlying JavaMail <code>Session</code> has to be
|
||||
* configured with the property <code>"mail.smtp.auth"</code> set to
|
||||
* <code>true</code>, else the specified username will not be sent to the
|
||||
* mail server by the JavaMail runtime. If you are not explicitly passing
|
||||
* in a <code>Session</code> to use, simply specify this setting via
|
||||
* {@link #setJavaMailProperties}.
|
||||
* @see #setSession
|
||||
* @see #setPassword
|
||||
*/
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the username for the account at the mail host.
|
||||
*/
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password for the account at the mail host, if any.
|
||||
* <p>Note that the underlying JavaMail <code>Session</code> has to be
|
||||
* configured with the property <code>"mail.smtp.auth"</code> set to
|
||||
* <code>true</code>, else the specified password will not be sent to the
|
||||
* mail server by the JavaMail runtime. If you are not explicitly passing
|
||||
* in a <code>Session</code> to use, simply specify this setting via
|
||||
* {@link #setJavaMailProperties}.
|
||||
* @see #setSession
|
||||
* @see #setUsername
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the password for the account at the mail host.
|
||||
*/
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default encoding to use for {@link MimeMessage MimeMessages}
|
||||
* created by this instance.
|
||||
* <p>Such an encoding will be auto-detected by {@link MimeMessageHelper}.
|
||||
*/
|
||||
public void setDefaultEncoding(String defaultEncoding) {
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default encoding for {@link MimeMessage MimeMessages},
|
||||
* or <code>null</code> if none.
|
||||
*/
|
||||
public String getDefaultEncoding() {
|
||||
return this.defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default Java Activation {@link FileTypeMap} to use for
|
||||
* {@link MimeMessage MimeMessages} created by this instance.
|
||||
* <p>A <code>FileTypeMap</code> specified here will be autodetected by
|
||||
* {@link MimeMessageHelper}, avoiding the need to specify the
|
||||
* <code>FileTypeMap</code> for each <code>MimeMessageHelper</code> instance.
|
||||
* <p>For example, you can specify a custom instance of Spring's
|
||||
* {@link ConfigurableMimeFileTypeMap} here. If not explicitly specified,
|
||||
* a default <code>ConfigurableMimeFileTypeMap</code> will be used, containing
|
||||
* an extended set of MIME type mappings (as defined by the
|
||||
* <code>mime.types</code> file contained in the Spring jar).
|
||||
* @see MimeMessageHelper#setFileTypeMap
|
||||
*/
|
||||
public void setDefaultFileTypeMap(FileTypeMap defaultFileTypeMap) {
|
||||
this.defaultFileTypeMap = defaultFileTypeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default Java Activation {@link FileTypeMap} for
|
||||
* {@link MimeMessage MimeMessages}, or <code>null</code> if none.
|
||||
*/
|
||||
public FileTypeMap getDefaultFileTypeMap() {
|
||||
return this.defaultFileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of MailSender
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void send(SimpleMailMessage simpleMessage) throws MailException {
|
||||
send(new SimpleMailMessage[] { simpleMessage });
|
||||
}
|
||||
|
||||
public void send(SimpleMailMessage[] simpleMessages) throws MailException {
|
||||
List<MimeMessage> mimeMessages = new ArrayList<MimeMessage>(simpleMessages.length);
|
||||
for (SimpleMailMessage simpleMessage : simpleMessages) {
|
||||
MimeMailMessage message = new MimeMailMessage(createMimeMessage());
|
||||
simpleMessage.copyTo(message);
|
||||
mimeMessages.add(message.getMimeMessage());
|
||||
}
|
||||
doSend(mimeMessages.toArray(new MimeMessage[mimeMessages.size()]), simpleMessages);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of JavaMailSender
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This implementation creates a SmartMimeMessage, holding the specified
|
||||
* default encoding and default FileTypeMap. This special defaults-carrying
|
||||
* message will be autodetected by {@link MimeMessageHelper}, which will use
|
||||
* the carried encoding and FileTypeMap unless explicitly overridden.
|
||||
* @see #setDefaultEncoding
|
||||
* @see #setDefaultFileTypeMap
|
||||
*/
|
||||
public MimeMessage createMimeMessage() {
|
||||
return new SmartMimeMessage(getSession(), getDefaultEncoding(), getDefaultFileTypeMap());
|
||||
}
|
||||
|
||||
public MimeMessage createMimeMessage(InputStream contentStream) throws MailException {
|
||||
try {
|
||||
return new MimeMessage(getSession(), contentStream);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException("Could not parse raw MIME content", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void send(MimeMessage mimeMessage) throws MailException {
|
||||
send(new MimeMessage[] {mimeMessage});
|
||||
}
|
||||
|
||||
public void send(MimeMessage[] mimeMessages) throws MailException {
|
||||
doSend(mimeMessages, null);
|
||||
}
|
||||
|
||||
public void send(MimeMessagePreparator mimeMessagePreparator) throws MailException {
|
||||
send(new MimeMessagePreparator[] { mimeMessagePreparator });
|
||||
}
|
||||
|
||||
public void send(MimeMessagePreparator[] mimeMessagePreparators) throws MailException {
|
||||
try {
|
||||
List<MimeMessage> mimeMessages = new ArrayList<MimeMessage>(mimeMessagePreparators.length);
|
||||
for (MimeMessagePreparator preparator : mimeMessagePreparators) {
|
||||
MimeMessage mimeMessage = createMimeMessage();
|
||||
preparator.prepare(mimeMessage);
|
||||
mimeMessages.add(mimeMessage);
|
||||
}
|
||||
send(mimeMessages.toArray(new MimeMessage[mimeMessages.size()]));
|
||||
}
|
||||
catch (MailException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MailPreparationException(ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MailPreparationException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actually send the given array of MimeMessages via JavaMail.
|
||||
* @param mimeMessages MimeMessage objects to send
|
||||
* @param originalMessages corresponding original message objects
|
||||
* that the MimeMessages have been created from (with same array
|
||||
* length and indices as the "mimeMessages" array), if any
|
||||
* @throws org.springframework.mail.MailAuthenticationException
|
||||
* in case of authentication failure
|
||||
* @throws org.springframework.mail.MailSendException
|
||||
* in case of failure when sending a message
|
||||
*/
|
||||
protected void doSend(MimeMessage[] mimeMessages, Object[] originalMessages) throws MailException {
|
||||
Map<Object, Exception> failedMessages = new LinkedHashMap<Object, Exception>();
|
||||
|
||||
Transport transport;
|
||||
try {
|
||||
transport = getTransport(getSession());
|
||||
transport.connect(getHost(), getPort(), getUsername(), getPassword());
|
||||
}
|
||||
catch (AuthenticationFailedException ex) {
|
||||
throw new MailAuthenticationException(ex);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
// Effectively, all messages failed...
|
||||
for (int i = 0; i < mimeMessages.length; i++) {
|
||||
Object original = (originalMessages != null ? originalMessages[i] : mimeMessages[i]);
|
||||
failedMessages.put(original, ex);
|
||||
}
|
||||
throw new MailSendException("Mail server connection failed", ex, failedMessages);
|
||||
}
|
||||
|
||||
try {
|
||||
for (int i = 0; i < mimeMessages.length; i++) {
|
||||
MimeMessage mimeMessage = mimeMessages[i];
|
||||
try {
|
||||
if (mimeMessage.getSentDate() == null) {
|
||||
mimeMessage.setSentDate(new Date());
|
||||
}
|
||||
String messageId = mimeMessage.getMessageID();
|
||||
mimeMessage.saveChanges();
|
||||
if (messageId != null) {
|
||||
// Preserve explicitly specified message id...
|
||||
mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId);
|
||||
}
|
||||
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
Object original = (originalMessages != null ? originalMessages[i] : mimeMessage);
|
||||
failedMessages.put(original, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
transport.close();
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
if (!failedMessages.isEmpty()) {
|
||||
throw new MailSendException("Failed to close server connection after message failures", ex,
|
||||
failedMessages);
|
||||
}
|
||||
else {
|
||||
throw new MailSendException("Failed to close server connection after message sending", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!failedMessages.isEmpty()) {
|
||||
throw new MailSendException(failedMessages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a Transport object from the given JavaMail Session,
|
||||
* using the configured protocol.
|
||||
* <p>Can be overridden in subclasses, e.g. to return a mock Transport object.
|
||||
* @see javax.mail.Session#getTransport(String)
|
||||
* @see #getProtocol()
|
||||
*/
|
||||
protected Transport getTransport(Session session) throws NoSuchProviderException {
|
||||
String protocol = getProtocol();
|
||||
if (protocol == null) {
|
||||
protocol = session.getProperty("mail.transport.protocol");
|
||||
if (protocol == null) {
|
||||
protocol = DEFAULT_PROTOCOL;
|
||||
}
|
||||
}
|
||||
return session.getTransport(protocol);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import org.springframework.mail.MailMessage;
|
||||
import org.springframework.mail.MailParseException;
|
||||
|
||||
/**
|
||||
* Implementation of the MailMessage interface for a JavaMail MIME message,
|
||||
* to let message population code interact with a simple message or a MIME
|
||||
* message through a common interface.
|
||||
*
|
||||
* <p>Uses a MimeMessageHelper underneath. Can either be created with a
|
||||
* MimeMessageHelper instance or with a JavaMail MimeMessage instance.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.5
|
||||
* @see MimeMessageHelper
|
||||
* @see javax.mail.internet.MimeMessage
|
||||
*/
|
||||
public class MimeMailMessage implements MailMessage {
|
||||
|
||||
private final MimeMessageHelper helper;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MimeMailMessage based on the given MimeMessageHelper.
|
||||
* @param mimeMessageHelper the MimeMessageHelper
|
||||
*/
|
||||
public MimeMailMessage(MimeMessageHelper mimeMessageHelper) {
|
||||
this.helper = mimeMessageHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new MimeMailMessage based on the given JavaMail MimeMessage.
|
||||
* @param mimeMessage the JavaMail MimeMessage
|
||||
*/
|
||||
public MimeMailMessage(MimeMessage mimeMessage) {
|
||||
this.helper = new MimeMessageHelper(mimeMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MimeMessageHelper that this MimeMailMessage is based on.
|
||||
*/
|
||||
public final MimeMessageHelper getMimeMessageHelper() {
|
||||
return this.helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JavaMail MimeMessage that this MimeMailMessage is based on.
|
||||
*/
|
||||
public final MimeMessage getMimeMessage() {
|
||||
return this.helper.getMimeMessage();
|
||||
}
|
||||
|
||||
|
||||
public void setFrom(String from) throws MailParseException {
|
||||
try {
|
||||
this.helper.setFrom(from);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setReplyTo(String replyTo) throws MailParseException {
|
||||
try {
|
||||
this.helper.setReplyTo(replyTo);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTo(String to) throws MailParseException {
|
||||
try {
|
||||
this.helper.setTo(to);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTo(String[] to) throws MailParseException {
|
||||
try {
|
||||
this.helper.setTo(to);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCc(String cc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setCc(cc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCc(String[] cc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setCc(cc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBcc(String bcc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setBcc(bcc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBcc(String[] bcc) throws MailParseException {
|
||||
try {
|
||||
this.helper.setBcc(bcc);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSentDate(Date sentDate) throws MailParseException {
|
||||
try {
|
||||
this.helper.setSentDate(sentDate);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSubject(String subject) throws MailParseException {
|
||||
try {
|
||||
this.helper.setSubject(subject);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void setText(String text) throws MailParseException {
|
||||
try {
|
||||
this.helper.setText(text);
|
||||
}
|
||||
catch (MessagingException ex) {
|
||||
throw new MailParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
* Callback interface for the preparation of JavaMail MIME messages.
|
||||
*
|
||||
* <p>The corresponding <code>send</code> methods of {@link JavaMailSender}
|
||||
* will take care of the actual creation of a {@link MimeMessage} instance,
|
||||
* and of proper exception conversion.
|
||||
*
|
||||
* <p>It is often convenient to use a {@link MimeMessageHelper} for populating
|
||||
* the passed-in MimeMessage, in particular when working with attachments or
|
||||
* special character encodings.
|
||||
* See {@link MimeMessageHelper MimeMessageHelper's javadoc} for an example.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.10.2003
|
||||
* @see JavaMailSender#send(MimeMessagePreparator)
|
||||
* @see JavaMailSender#send(MimeMessagePreparator[])
|
||||
* @see MimeMessageHelper
|
||||
*/
|
||||
public interface MimeMessagePreparator {
|
||||
|
||||
/**
|
||||
* Prepare the given new MimeMessage instance.
|
||||
* @param mimeMessage the message to prepare
|
||||
* @throws javax.mail.MessagingException passing any exceptions thrown by MimeMessage
|
||||
* methods through for automatic conversion to the MailException hierarchy
|
||||
* @throws java.io.IOException passing any exceptions thrown by MimeMessage methods
|
||||
* through for automatic conversion to the MailException hierarchy
|
||||
* @throws Exception if mail preparation failed, for example when a
|
||||
* Velocity template cannot be rendered for the mail text
|
||||
*/
|
||||
void prepare(MimeMessage mimeMessage) throws Exception;
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
/**
|
||||
* Special subclass of the standard JavaMail {@link MimeMessage}, carrying a
|
||||
* default encoding to be used when populating the message and a default Java
|
||||
* Activation {@link FileTypeMap} to be used for resolving attachment types.
|
||||
*
|
||||
* <p>Created by {@link JavaMailSenderImpl} in case of a specified default encoding
|
||||
* and/or default FileTypeMap. Autodetected by {@link MimeMessageHelper}, which
|
||||
* will use the carried encoding and FileTypeMap unless explicitly overridden.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.2
|
||||
* @see JavaMailSenderImpl#createMimeMessage()
|
||||
* @see MimeMessageHelper#getDefaultEncoding(javax.mail.internet.MimeMessage)
|
||||
* @see MimeMessageHelper#getDefaultFileTypeMap(javax.mail.internet.MimeMessage)
|
||||
*/
|
||||
class SmartMimeMessage extends MimeMessage {
|
||||
|
||||
private final String defaultEncoding;
|
||||
|
||||
private final FileTypeMap defaultFileTypeMap;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SmartMimeMessage.
|
||||
* @param session the JavaMail Session to create the message for
|
||||
* @param defaultEncoding the default encoding, or <code>null</code> if none
|
||||
* @param defaultFileTypeMap the default FileTypeMap, or <code>null</code> if none
|
||||
*/
|
||||
public SmartMimeMessage(Session session, String defaultEncoding, FileTypeMap defaultFileTypeMap) {
|
||||
super(session);
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
this.defaultFileTypeMap = defaultFileTypeMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the default encoding of this message, or <code>null</code> if none.
|
||||
*/
|
||||
public final String getDefaultEncoding() {
|
||||
return this.defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default FileTypeMap of this message, or <code>null</code> if none.
|
||||
*/
|
||||
public final FileTypeMap getDefaultFileTypeMap() {
|
||||
return this.defaultFileTypeMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright 2002-2010 the original author or authors.
|
||||
#
|
||||
# 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.
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# Defaults for the Java Activation Framework
|
||||
# Additional extensions registered in this file:
|
||||
# text/plain java c c++ pl cc h
|
||||
#
|
||||
################################################################################
|
||||
|
||||
text/html html htm HTML HTM
|
||||
text/plain txt text TXT TEXT java c c++ pl cc h
|
||||
image/gif gif GIF
|
||||
image/ief ief
|
||||
image/jpeg jpeg jpg jpe JPG
|
||||
image/tiff tiff tif
|
||||
image/x-xwindowdump xwd
|
||||
application/postscript ai eps ps
|
||||
application/rtf rtf
|
||||
application/x-tex tex
|
||||
application/x-texinfo texinfo texi
|
||||
application/x-troff t tr roff
|
||||
audio/basic au
|
||||
audio/midi midi mid
|
||||
audio/x-aifc aifc
|
||||
audio/x-aiff aif aiff
|
||||
audio/x-mpeg mpeg mpg
|
||||
audio/x-wav wav
|
||||
video/mpeg mpeg mpg mpe
|
||||
video/quicktime qt mov
|
||||
video/x-msvideo avi
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# Additional file types adapted from
|
||||
# http://www.utoronto.ca/webdocs/HTMLdocs/Book/Book-3ed/appb/mimetype.html
|
||||
# kindly re-licensed to Apache Software License 2.0 by Ian Graham.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# TEXT TYPES
|
||||
|
||||
text/x-speech talk
|
||||
text/css css
|
||||
text/csv csv
|
||||
|
||||
# IMAGE TYPES
|
||||
|
||||
# X-Windows bitmap (b/w)
|
||||
image/x-xbitmap xbm
|
||||
# X-Windows pixelmap (8-bit color)
|
||||
image/x-xpixmap xpm
|
||||
# Portable Network Graphics
|
||||
image/x-png png
|
||||
# Image Exchange Format (RFC 1314)
|
||||
image/ief ief
|
||||
# JPEG
|
||||
image/jpeg jpeg jpg jpe
|
||||
# RGB
|
||||
image/rgb rgb
|
||||
# Group III Fax (RFC 1494)
|
||||
image/g3fax g3f
|
||||
# X Windowdump format
|
||||
image/x-xwindowdump xwd
|
||||
# Macintosh PICT format
|
||||
image/x-pict pict
|
||||
# PPM (UNIX PPM package)
|
||||
image/x-portable-pixmap ppm
|
||||
# PGM (UNIX PPM package)
|
||||
image/x-portable-graymap pgm
|
||||
# PBM (UNIX PPM package)
|
||||
image/x-portable-bitmap pbm
|
||||
# PNM (UNIX PPM package)
|
||||
image/x-portable-anymap pnm
|
||||
# Microsoft Windows bitmap
|
||||
image/x-ms-bmp bmp
|
||||
# CMU raster
|
||||
image/x-cmu-raster ras
|
||||
# Kodak Photo-CD
|
||||
image/x-photo-cd pcd
|
||||
# Computer Graphics Metafile
|
||||
image/cgm cgm
|
||||
# CALS Type 1 or 2
|
||||
image/x-cals mil cal
|
||||
# Fractal Image Format (Iterated Systems)
|
||||
image/fif fif
|
||||
# QuickSilver active image (Micrografx)
|
||||
image/x-mgx-dsf dsf
|
||||
# CMX vector image (Corel)
|
||||
image/x-cmx cmx
|
||||
# Wavelet-compressed (Summus)
|
||||
image/wavelet wi
|
||||
# AutoCad Drawing (SoftSource)
|
||||
image/vnd.dwg dwg
|
||||
# AutoCad DXF file (SoftSource)
|
||||
image/vnd.dxf dxf
|
||||
# Simple Vector Format (SoftSource)
|
||||
image/vnd.svf svf
|
||||
|
||||
# AUDIO/VOICE/MUSIC RELATED TYPES
|
||||
|
||||
# """basic""audio - 8-bit u-law PCM"
|
||||
audio/basic au snd
|
||||
# Macintosh audio format (AIpple)
|
||||
audio/x-aiff aif aiff aifc
|
||||
# Microsoft audio
|
||||
audio/x-wav wav
|
||||
# MPEG audio
|
||||
audio/x-mpeg mpa abs mpega
|
||||
# MPEG-2 audio
|
||||
audio/x-mpeg-2 mp2a mpa2
|
||||
# compressed speech (Echo Speech Corp.)
|
||||
audio/echospeech es
|
||||
# Toolvox speech audio (Voxware)
|
||||
audio/voxware vox
|
||||
# RapidTransit compressed audio (Fast Man)
|
||||
application/fastman lcc
|
||||
# Realaudio (Progressive Networks)
|
||||
application/x-pn-realaudio ra ram
|
||||
# MIDI music data
|
||||
x-music/x-midi mmid
|
||||
# Koan music data (SSeyo)
|
||||
application/vnd.koan skp
|
||||
# Speech synthesis data (MVP Solutions)
|
||||
text/x-speech talk
|
||||
|
||||
# VIDEO TYPES
|
||||
|
||||
# MPEG video
|
||||
video/mpeg mpeg mpg mpe
|
||||
# MPEG-2 video
|
||||
video/mpeg-2 mpv2 mp2v
|
||||
# Macintosh Quicktime
|
||||
video/quicktime qt mov
|
||||
# Microsoft video
|
||||
video/x-msvideo avi
|
||||
# SGI Movie format
|
||||
video/x-sgi-movie movie
|
||||
# VDOlive streaming video (VDOnet)
|
||||
video/vdo vdo
|
||||
# Vivo streaming video (Vivo software)
|
||||
video/vnd.vivo viv
|
||||
|
||||
# SPECIAL HTTP/WEB APPLICATION TYPES
|
||||
|
||||
# Proxy autoconfiguration (Netscape browsers)
|
||||
application/x-ns-proxy-autoconfig pac
|
||||
# Netscape Cooltalk chat data (Netscape)
|
||||
x-conference/x-cooltalk ice
|
||||
|
||||
# TEXT-RELATED
|
||||
|
||||
# PostScript
|
||||
application/postscript ai eps ps
|
||||
# Microsoft Rich Text Format
|
||||
application/rtf rtf
|
||||
# Adobe Acrobat PDF
|
||||
application/pdf pdf
|
||||
# Maker Interchange Format (FrameMaker)
|
||||
application/vnd.mif mif
|
||||
# Troff document
|
||||
application/x-troff t tr roff
|
||||
# Troff document with MAN macros
|
||||
application/x-troff-man man
|
||||
# Troff document with ME macros
|
||||
application/x-troff-me me
|
||||
# Troff document with MS macros
|
||||
application/x-troff-ms ms
|
||||
# LaTeX document
|
||||
application/x-latex latex
|
||||
# Tex/LateX document
|
||||
application/x-tex tex
|
||||
# GNU TexInfo document
|
||||
application/x-texinfo texinfo texi
|
||||
# TeX dvi format
|
||||
application/x-dvi dvi
|
||||
# MS word document
|
||||
application/msword doc DOC
|
||||
# Office Document Architecture
|
||||
application/oda oda
|
||||
# Envoy Document
|
||||
application/envoy evy
|
||||
|
||||
# ARCHIVE/COMPRESSED ARCHIVES
|
||||
|
||||
# Gnu tar format
|
||||
application/x-gtar gtar
|
||||
# 4.3BSD tar format
|
||||
application/x-tar tar
|
||||
# POSIX tar format
|
||||
application/x-ustar ustar
|
||||
# Old CPIO format
|
||||
application/x-bcpio bcpio
|
||||
# POSIX CPIO format
|
||||
application/x-cpio cpio
|
||||
# UNIX sh shell archive
|
||||
application/x-shar shar
|
||||
# DOS/PC - Pkzipped archive
|
||||
application/zip zip
|
||||
# Macintosh Binhexed archive
|
||||
application/mac-binhex40 hqx
|
||||
# Macintosh Stuffit Archive
|
||||
application/x-stuffit sit sea
|
||||
# Fractal Image Format
|
||||
application/fractals fif
|
||||
# "Binary UUencoded"
|
||||
application/octet-stream bin uu
|
||||
# PC executable
|
||||
application/octet-stream exe
|
||||
# "WAIS ""sources"""
|
||||
application/x-wais-source src wsrc
|
||||
# NCSA HDF data format
|
||||
application/hdf hdf
|
||||
|
||||
# DOWNLOADABLE PROGRAM/SCRIPTS
|
||||
|
||||
# Javascript program
|
||||
text/javascript js ls mocha
|
||||
# UNIX bourne shell program
|
||||
application/x-sh sh
|
||||
# UNIX c-shell program
|
||||
application/x-csh csh
|
||||
# Perl program
|
||||
application/x-perl pl
|
||||
# Tcl (Tool Control Language) program
|
||||
application/x-tcl tcl
|
||||
|
||||
# ANIMATION/MULTIMEDIA
|
||||
|
||||
# FutureSplash vector animation (FutureWave)
|
||||
application/futuresplash spl
|
||||
# mBED multimedia data (mBED)
|
||||
application/mbedlet mbd
|
||||
# PowerMedia multimedia (RadMedia)
|
||||
application/x-rad-powermedia rad
|
||||
|
||||
# PRESENTATION
|
||||
|
||||
# PowerPoint presentation (Microsoft)
|
||||
application/mspowerpoint ppz
|
||||
# ASAP WordPower (Software Publishing Corp.)
|
||||
application/x-asap asp
|
||||
# Astound Web Player multimedia data (GoldDisk)
|
||||
application/astound asn
|
||||
|
||||
# SPECIAL EMBEDDED OBJECT
|
||||
|
||||
# OLE script e.g. Visual Basic (Ncompass)
|
||||
application/x-olescript axs
|
||||
# OLE Object (Microsoft/NCompass)
|
||||
application/x-oleobject ods
|
||||
# OpenScape OLE/OCX objects (Business@Web)
|
||||
x-form/x-openscape opp
|
||||
# Visual Basic objects (Amara)
|
||||
application/x-webbasic wba
|
||||
# Specialized data entry forms (Alpha Software)
|
||||
application/x-alpha-form frm
|
||||
# client-server objects (Wayfarer Communications)
|
||||
x-script/x-wfxclient wfx
|
||||
|
||||
# GENERAL APPLICATIONS
|
||||
|
||||
# Undefined binary data (often executable progs)
|
||||
application/octet-stream exe com
|
||||
# Pointcast news data (Pointcast)
|
||||
application/x-pcn pcn
|
||||
# Excel spreadsheet (Microsoft)
|
||||
application/vnd.ms-excel xls
|
||||
# PowerPoint (Microsoft)
|
||||
application/vnd.ms-powerpoint ppt
|
||||
# Microsoft Project (Microsoft)
|
||||
application/vnd.ms-project mpp
|
||||
# SourceView document (Dataware Electronics)
|
||||
application/vnd.svd svd
|
||||
# Net Install - software install (20/20 Software)
|
||||
application/x-net-install ins
|
||||
# Carbon Copy - remote control/access (Microcom)
|
||||
application/ccv ccv
|
||||
# Spreadsheets (Visual Components)
|
||||
workbook/formulaone vts
|
||||
|
||||
# 2D/3D DATA/VIRTUAL REALITY TYPES
|
||||
|
||||
# VRML data file
|
||||
x-world/x-vrml wrl vrml
|
||||
# WIRL - VRML data (VREAM)
|
||||
x-world/x-vream vrw
|
||||
# Play3D 3d scene data (Play3D)
|
||||
application/x-p3d p3d
|
||||
# Viscape Interactive 3d world data (Superscape)
|
||||
x-world/x-svr svr
|
||||
# WebActive 3d data (Plastic Thought)
|
||||
x-world/x-wvr wvr
|
||||
# QuickDraw3D scene data (Apple)
|
||||
x-world/x-3dmf 3dmf
|
||||
|
||||
# SCIENTIFIC/MATH/CAD TYPES
|
||||
|
||||
# Mathematica notebook
|
||||
application/mathematica ma
|
||||
# Computational meshes for numerical simulations
|
||||
x-model/x-mesh msh
|
||||
# Vis5D 5-dimensional data
|
||||
application/vis5d v5d
|
||||
# IGES models -- CAD/CAM (CGM) data
|
||||
application/iges igs
|
||||
# Autocad WHIP vector drawings
|
||||
drawing/x-dwf dwf
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* JavaMail support for Spring's mail infrastructure.
|
||||
* Provides an extended JavaMailSender interface and a MimeMessageHelper
|
||||
* class for convenient population of a JavaMail MimeMessage.
|
||||
*
|
||||
*/
|
||||
package org.springframework.mail.javamail;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Spring's generic mail infrastructure.
|
||||
* Concrete implementations are provided in the subpackages.
|
||||
*
|
||||
*/
|
||||
package org.springframework.mail;
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.commonj;
|
||||
|
||||
import commonj.timers.Timer;
|
||||
import commonj.timers.TimerListener;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple TimerListener adapter that delegates to a given Runnable.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see commonj.timers.TimerListener
|
||||
* @see java.lang.Runnable
|
||||
*/
|
||||
public class DelegatingTimerListener implements TimerListener {
|
||||
|
||||
private final Runnable runnable;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DelegatingTimerListener.
|
||||
* @param runnable the Runnable implementation to delegate to
|
||||
*/
|
||||
public DelegatingTimerListener(Runnable runnable) {
|
||||
Assert.notNull(runnable, "Runnable is required");
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates execution to the underlying Runnable.
|
||||
*/
|
||||
public void timerExpired(Timer timer) {
|
||||
this.runnable.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.commonj;
|
||||
|
||||
import commonj.work.Work;
|
||||
|
||||
import org.springframework.scheduling.SchedulingAwareRunnable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple Work adapter that delegates to a given Runnable.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see commonj.work.Work
|
||||
* @see java.lang.Runnable
|
||||
*/
|
||||
public class DelegatingWork implements Work {
|
||||
|
||||
private final Runnable delegate;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DelegatingWork.
|
||||
* @param delegate the Runnable implementation to delegate to
|
||||
* (may be a SchedulingAwareRunnable for extended support)
|
||||
* @see org.springframework.scheduling.SchedulingAwareRunnable
|
||||
* @see #isDaemon()
|
||||
*/
|
||||
public DelegatingWork(Runnable delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped Runnable implementation.
|
||||
*/
|
||||
public final Runnable getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates execution to the underlying Runnable.
|
||||
*/
|
||||
public void run() {
|
||||
this.delegate.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation delegates to
|
||||
* {@link org.springframework.scheduling.SchedulingAwareRunnable#isLongLived()},
|
||||
* if available.
|
||||
*/
|
||||
public boolean isDaemon() {
|
||||
return (this.delegate instanceof SchedulingAwareRunnable &&
|
||||
((SchedulingAwareRunnable) this.delegate).isLongLived());
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation is empty, since we expect the Runnable
|
||||
* to terminate based on some specific shutdown signal.
|
||||
*/
|
||||
public void release() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.commonj;
|
||||
|
||||
import commonj.timers.TimerListener;
|
||||
|
||||
/**
|
||||
* JavaBean that describes a scheduled TimerListener, consisting of
|
||||
* the TimerListener itself (or a Runnable to create a TimerListener for)
|
||||
* and a delay plus period. Period needs to be specified;
|
||||
* there is no point in a default for it.
|
||||
*
|
||||
* <p>The CommonJ TimerManager does not offer more sophisticated scheduling
|
||||
* options such as cron expressions. Consider using Quartz for such
|
||||
* advanced needs.
|
||||
*
|
||||
* <p>Note that the TimerManager uses a TimerListener instance that is
|
||||
* shared between repeated executions, in contrast to Quartz which
|
||||
* instantiates a new Job for each execution.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see commonj.timers.TimerListener
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
* @see commonj.timers.TimerManager#scheduleAtFixedRate(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public class ScheduledTimerListener {
|
||||
|
||||
private TimerListener timerListener;
|
||||
|
||||
private long delay = 0;
|
||||
|
||||
private long period = -1;
|
||||
|
||||
private boolean fixedRate = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener,
|
||||
* to be populated via bean properties.
|
||||
* @see #setTimerListener
|
||||
* @see #setDelay
|
||||
* @see #setPeriod
|
||||
* @see #setFixedRate
|
||||
*/
|
||||
public ScheduledTimerListener() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution without delay.
|
||||
* @param timerListener the TimerListener to schedule
|
||||
*/
|
||||
public ScheduledTimerListener(TimerListener timerListener) {
|
||||
this.timerListener = timerListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution with the given delay.
|
||||
* @param timerListener the TimerListener to schedule
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
*/
|
||||
public ScheduledTimerListener(TimerListener timerListener, long delay) {
|
||||
this.timerListener = timerListener;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener.
|
||||
* @param timerListener the TimerListener to schedule
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
* @param period the period between repeated task executions (ms)
|
||||
* @param fixedRate whether to schedule as fixed-rate execution
|
||||
*/
|
||||
public ScheduledTimerListener(TimerListener timerListener, long delay, long period, boolean fixedRate) {
|
||||
this.timerListener = timerListener;
|
||||
this.delay = delay;
|
||||
this.period = period;
|
||||
this.fixedRate = fixedRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution without delay.
|
||||
* @param timerTask the Runnable to schedule as TimerListener
|
||||
*/
|
||||
public ScheduledTimerListener(Runnable timerTask) {
|
||||
setRunnable(timerTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener, with default
|
||||
* one-time execution with the given delay.
|
||||
* @param timerTask the Runnable to schedule as TimerListener
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
*/
|
||||
public ScheduledTimerListener(Runnable timerTask, long delay) {
|
||||
setRunnable(timerTask);
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ScheduledTimerListener.
|
||||
* @param timerTask the Runnable to schedule as TimerListener
|
||||
* @param delay the delay before starting the task for the first time (ms)
|
||||
* @param period the period between repeated task executions (ms)
|
||||
* @param fixedRate whether to schedule as fixed-rate execution
|
||||
*/
|
||||
public ScheduledTimerListener(Runnable timerTask, long delay, long period, boolean fixedRate) {
|
||||
setRunnable(timerTask);
|
||||
this.delay = delay;
|
||||
this.period = period;
|
||||
this.fixedRate = fixedRate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the Runnable to schedule as TimerListener.
|
||||
* @see DelegatingTimerListener
|
||||
*/
|
||||
public void setRunnable(Runnable timerTask) {
|
||||
this.timerListener = new DelegatingTimerListener(timerTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TimerListener to schedule.
|
||||
*/
|
||||
public void setTimerListener(TimerListener timerListener) {
|
||||
this.timerListener = timerListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TimerListener to schedule.
|
||||
*/
|
||||
public TimerListener getTimerListener() {
|
||||
return this.timerListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the delay before starting the task for the first time,
|
||||
* in milliseconds. Default is 0, immediately starting the
|
||||
* task after successful scheduling.
|
||||
* <p>If the "firstTime" property is specified, this property will be ignored.
|
||||
* Specify one or the other, not both.
|
||||
*/
|
||||
public void setDelay(long delay) {
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the delay before starting the job for the first time.
|
||||
*/
|
||||
public long getDelay() {
|
||||
return this.delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the period between repeated task executions, in milliseconds.
|
||||
* <p>Default is -1, leading to one-time execution. In case of zero or a
|
||||
* positive value, the task will be executed repeatedly, with the given
|
||||
* interval inbetween executions.
|
||||
* <p>Note that the semantics of the period value vary between fixed-rate
|
||||
* and fixed-delay execution.
|
||||
* <p><b>Note:</b> A period of 0 (for example as fixed delay) <i>is</i>
|
||||
* supported, because the CommonJ specification defines this as a legal value.
|
||||
* Hence a value of 0 will result in immediate re-execution after a job has
|
||||
* finished (not in one-time execution like with <code>java.util.Timer</code>).
|
||||
* @see #setFixedRate
|
||||
* @see #isOneTimeTask()
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public void setPeriod(long period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the period between repeated task executions.
|
||||
*/
|
||||
public long getPeriod() {
|
||||
return this.period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this task only ever going to execute once?
|
||||
* @return <code>true</code> if this task is only ever going to execute once
|
||||
* @see #getPeriod()
|
||||
*/
|
||||
public boolean isOneTimeTask() {
|
||||
return (this.period < 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to schedule as fixed-rate execution, rather than
|
||||
* fixed-delay execution. Default is "false", i.e. fixed delay.
|
||||
* <p>See TimerManager javadoc for details on those execution modes.
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
* @see commonj.timers.TimerManager#scheduleAtFixedRate(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public void setFixedRate(boolean fixedRate) {
|
||||
this.fixedRate = fixedRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to schedule as fixed-rate execution.
|
||||
*/
|
||||
public boolean isFixedRate() {
|
||||
return this.fixedRate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.commonj;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import commonj.timers.TimerManager;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
|
||||
/**
|
||||
* Base class for classes that are accessing a CommonJ {@link commonj.timers.TimerManager}
|
||||
* Defines common configuration settings and common lifecycle handling.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see commonj.timers.TimerManager
|
||||
*/
|
||||
public abstract class TimerManagerAccessor extends JndiLocatorSupport
|
||||
implements InitializingBean, DisposableBean, Lifecycle {
|
||||
|
||||
private TimerManager timerManager;
|
||||
|
||||
private String timerManagerName;
|
||||
|
||||
private boolean shared = false;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the CommonJ TimerManager to delegate to.
|
||||
* <p>Note that the given TimerManager's lifecycle will be managed
|
||||
* by this FactoryBean.
|
||||
* <p>Alternatively (and typically), you can specify the JNDI name
|
||||
* of the target TimerManager.
|
||||
* @see #setTimerManagerName
|
||||
*/
|
||||
public void setTimerManager(TimerManager timerManager) {
|
||||
this.timerManager = timerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JNDI name of the CommonJ TimerManager.
|
||||
* <p>This can either be a fully qualified JNDI name, or the JNDI name relative
|
||||
* to the current environment naming context if "resourceRef" is set to "true".
|
||||
* @see #setTimerManager
|
||||
* @see #setResourceRef
|
||||
*/
|
||||
public void setTimerManagerName(String timerManagerName) {
|
||||
this.timerManagerName = timerManagerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the TimerManager obtained by this FactoryBean
|
||||
* is a shared instance ("true") or an independent instance ("false").
|
||||
* The lifecycle of the former is supposed to be managed by the application
|
||||
* server, while the lifecycle of the latter is up to the application.
|
||||
* <p>Default is "false", i.e. managing an independent TimerManager instance.
|
||||
* This is what the CommonJ specification suggests that application servers
|
||||
* are supposed to offer via JNDI lookups, typically declared as a
|
||||
* <code>resource-ref</code> of type <code>commonj.timers.TimerManager</code>
|
||||
* in <code>web.xml<code>, with <code>res-sharing-scope</code> set to 'Unshareable'.
|
||||
* <p>Switch this flag to "true" if you are obtaining a shared TimerManager,
|
||||
* typically through specifying the JNDI location of a TimerManager that
|
||||
* has been explicitly declared as 'Shareable'. Note that WebLogic's
|
||||
* cluster-aware Job Scheduler is a shared TimerManager too.
|
||||
* <p>The sole difference between this FactoryBean being in shared or
|
||||
* non-shared mode is that it will only attempt to suspend / resume / stop
|
||||
* the underlying TimerManager in case of an independent (non-shared) instance.
|
||||
* This only affects the {@link org.springframework.context.Lifecycle} support
|
||||
* as well as application context shutdown.
|
||||
* @see #stop()
|
||||
* @see #start()
|
||||
* @see #destroy()
|
||||
* @see commonj.timers.TimerManager
|
||||
*/
|
||||
public void setShared(boolean shared) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
if (this.timerManager == null) {
|
||||
if (this.timerManagerName == null) {
|
||||
throw new IllegalArgumentException("Either 'timerManager' or 'timerManagerName' must be specified");
|
||||
}
|
||||
this.timerManager = lookup(this.timerManagerName, TimerManager.class);
|
||||
}
|
||||
}
|
||||
|
||||
protected final TimerManager getTimerManager() {
|
||||
return this.timerManager;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of Lifecycle interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resumes the underlying TimerManager (if not shared).
|
||||
* @see commonj.timers.TimerManager#resume()
|
||||
*/
|
||||
public void start() {
|
||||
if (!this.shared) {
|
||||
this.timerManager.resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suspends the underlying TimerManager (if not shared).
|
||||
* @see commonj.timers.TimerManager#suspend()
|
||||
*/
|
||||
public void stop() {
|
||||
if (!this.shared) {
|
||||
this.timerManager.suspend();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers the underlying TimerManager as running if it is
|
||||
* neither suspending nor stopping.
|
||||
* @see commonj.timers.TimerManager#isSuspending()
|
||||
* @see commonj.timers.TimerManager#isStopping()
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return (!this.timerManager.isSuspending() && !this.timerManager.isStopping());
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of DisposableBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Stops the underlying TimerManager (if not shared).
|
||||
* @see commonj.timers.TimerManager#stop()
|
||||
*/
|
||||
public void destroy() {
|
||||
// Stop the entire TimerManager, if necessary.
|
||||
if (!this.shared) {
|
||||
// May return early, but at least we already cancelled all known Timers.
|
||||
this.timerManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.commonj;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import commonj.timers.Timer;
|
||||
import commonj.timers.TimerManager;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.Lifecycle;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} that retrieves a
|
||||
* CommonJ {@link commonj.timers.TimerManager} and exposes it for bean references.
|
||||
*
|
||||
* <p><b>This is the central convenience class for setting up a
|
||||
* CommonJ TimerManager in a Spring context.</b>
|
||||
*
|
||||
* <p>Allows for registration of ScheduledTimerListeners. This is the main
|
||||
* purpose of this class; the TimerManager itself could also be fetched
|
||||
* from JNDI via {@link org.springframework.jndi.JndiObjectFactoryBean}.
|
||||
* In scenarios that just require static registration of tasks at startup,
|
||||
* there is no need to access the TimerManager itself in application code.
|
||||
*
|
||||
* <p>Note that the TimerManager uses a TimerListener instance that is
|
||||
* shared between repeated executions, in contrast to Quartz which
|
||||
* instantiates a new Job for each execution.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see ScheduledTimerListener
|
||||
* @see commonj.timers.TimerManager
|
||||
* @see commonj.timers.TimerListener
|
||||
*/
|
||||
public class TimerManagerFactoryBean extends TimerManagerAccessor
|
||||
implements FactoryBean<TimerManager>, InitializingBean, DisposableBean, Lifecycle {
|
||||
|
||||
private ScheduledTimerListener[] scheduledTimerListeners;
|
||||
|
||||
private final List<Timer> timers = new LinkedList<Timer>();
|
||||
|
||||
|
||||
/**
|
||||
* Register a list of ScheduledTimerListener objects with the TimerManager
|
||||
* that this FactoryBean creates. Depending on each ScheduledTimerListener's settings,
|
||||
* it will be registered via one of TimerManager's schedule methods.
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long)
|
||||
* @see commonj.timers.TimerManager#schedule(commonj.timers.TimerListener, long, long)
|
||||
* @see commonj.timers.TimerManager#scheduleAtFixedRate(commonj.timers.TimerListener, long, long)
|
||||
*/
|
||||
public void setScheduledTimerListeners(ScheduledTimerListener[] scheduledTimerListeners) {
|
||||
this.scheduledTimerListeners = scheduledTimerListeners;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of InitializingBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
super.afterPropertiesSet();
|
||||
if (this.scheduledTimerListeners != null) {
|
||||
TimerManager timerManager = getTimerManager();
|
||||
for (ScheduledTimerListener scheduledTask : this.scheduledTimerListeners) {
|
||||
Timer timer;
|
||||
if (scheduledTask.isOneTimeTask()) {
|
||||
timer = timerManager.schedule(scheduledTask.getTimerListener(), scheduledTask.getDelay());
|
||||
}
|
||||
else {
|
||||
if (scheduledTask.isFixedRate()) {
|
||||
timer = timerManager.scheduleAtFixedRate(
|
||||
scheduledTask.getTimerListener(), scheduledTask.getDelay(), scheduledTask.getPeriod());
|
||||
}
|
||||
else {
|
||||
timer = timerManager.schedule(
|
||||
scheduledTask.getTimerListener(), scheduledTask.getDelay(), scheduledTask.getPeriod());
|
||||
}
|
||||
}
|
||||
this.timers.add(timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of FactoryBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public TimerManager getObject() {
|
||||
return getTimerManager();
|
||||
}
|
||||
|
||||
public Class<? extends TimerManager> getObjectType() {
|
||||
TimerManager timerManager = getTimerManager();
|
||||
return (timerManager != null ? timerManager.getClass() : TimerManager.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of DisposableBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Cancels all statically registered Timers on shutdown,
|
||||
* and stops the underlying TimerManager (if not shared).
|
||||
* @see commonj.timers.Timer#cancel()
|
||||
* @see commonj.timers.TimerManager#stop()
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
// Cancel all registered timers.
|
||||
for (Timer timer : this.timers) {
|
||||
try {
|
||||
timer.cancel();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.warn("Could not cancel CommonJ Timer", ex);
|
||||
}
|
||||
}
|
||||
this.timers.clear();
|
||||
|
||||
// Stop the TimerManager itself.
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.commonj;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.Delayed;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import commonj.timers.Timer;
|
||||
import commonj.timers.TimerListener;
|
||||
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.SimpleTriggerContext;
|
||||
import org.springframework.scheduling.support.TaskUtils;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
|
||||
/**
|
||||
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
* a CommonJ {@link commonj.timers.TimerManager}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public class TimerManagerTaskScheduler extends TimerManagerAccessor implements TaskScheduler {
|
||||
|
||||
private volatile ErrorHandler errorHandler;
|
||||
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
public ScheduledFuture schedule(Runnable task, Trigger trigger) {
|
||||
return new ReschedulingTimerListener(errorHandlingTask(task, true), trigger).schedule();
|
||||
}
|
||||
|
||||
public ScheduledFuture schedule(Runnable task, Date startTime) {
|
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, false));
|
||||
Timer timer = getTimerManager().schedule(futureTask, startTime);
|
||||
futureTask.setTimer(timer);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period) {
|
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true));
|
||||
Timer timer = getTimerManager().scheduleAtFixedRate(futureTask, startTime, period);
|
||||
futureTask.setTimer(timer);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
public ScheduledFuture scheduleAtFixedRate(Runnable task, long period) {
|
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true));
|
||||
Timer timer = getTimerManager().scheduleAtFixedRate(futureTask, 0, period);
|
||||
futureTask.setTimer(timer);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
public ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay) {
|
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true));
|
||||
Timer timer = getTimerManager().schedule(futureTask, startTime, delay);
|
||||
futureTask.setTimer(timer);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
public ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay) {
|
||||
TimerScheduledFuture futureTask = new TimerScheduledFuture(errorHandlingTask(task, true));
|
||||
Timer timer = getTimerManager().schedule(futureTask, 0, delay);
|
||||
futureTask.setTimer(timer);
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
private Runnable errorHandlingTask(Runnable delegate, boolean isRepeatingTask) {
|
||||
return TaskUtils.decorateTaskWithErrorHandler(delegate, this.errorHandler, isRepeatingTask);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ScheduledFuture adapter that wraps a CommonJ Timer.
|
||||
*/
|
||||
private static class TimerScheduledFuture extends FutureTask<Object> implements TimerListener, ScheduledFuture<Object> {
|
||||
|
||||
protected transient Timer timer;
|
||||
|
||||
protected transient boolean cancelled = false;
|
||||
|
||||
public TimerScheduledFuture(Runnable runnable) {
|
||||
super(runnable, null);
|
||||
}
|
||||
|
||||
public void setTimer(Timer timer) {
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
public void timerExpired(Timer timer) {
|
||||
runAndReset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
boolean result = super.cancel(mayInterruptIfRunning);
|
||||
this.timer.cancel();
|
||||
this.cancelled = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return unit.convert(System.currentTimeMillis() - this.timer.getScheduledExecutionTime(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public int compareTo(Delayed other) {
|
||||
if (this == other) {
|
||||
return 0;
|
||||
}
|
||||
long diff = getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS);
|
||||
return (diff == 0 ? 0 : ((diff < 0)? -1 : 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ScheduledFuture adapter for trigger-based rescheduling.
|
||||
*/
|
||||
private class ReschedulingTimerListener extends TimerScheduledFuture {
|
||||
|
||||
private final Trigger trigger;
|
||||
|
||||
private final SimpleTriggerContext triggerContext = new SimpleTriggerContext();
|
||||
|
||||
private volatile Date scheduledExecutionTime;
|
||||
|
||||
public ReschedulingTimerListener(Runnable runnable, Trigger trigger) {
|
||||
super(runnable);
|
||||
this.trigger = trigger;
|
||||
}
|
||||
|
||||
public ScheduledFuture schedule() {
|
||||
this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
|
||||
if (this.scheduledExecutionTime == null) {
|
||||
return null;
|
||||
}
|
||||
setTimer(getTimerManager().schedule(this, this.scheduledExecutionTime));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timerExpired(Timer timer) {
|
||||
Date actualExecutionTime = new Date();
|
||||
super.timerExpired(timer);
|
||||
Date completionTime = new Date();
|
||||
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
|
||||
if (!this.cancelled) {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.commonj;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import commonj.work.Work;
|
||||
import commonj.work.WorkException;
|
||||
import commonj.work.WorkItem;
|
||||
import commonj.work.WorkListener;
|
||||
import commonj.work.WorkManager;
|
||||
import commonj.work.WorkRejectedException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
import org.springframework.scheduling.SchedulingException;
|
||||
import org.springframework.scheduling.SchedulingTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* TaskExecutor implementation that delegates to a CommonJ WorkManager,
|
||||
* implementing the {@link commonj.work.WorkManager} interface,
|
||||
* which either needs to be specified as reference or through the JNDI name.
|
||||
*
|
||||
* <p><b>This is the central convenience class for setting up a
|
||||
* CommonJ WorkManager in a Spring context.</b>
|
||||
*
|
||||
* <p>Also implements the CommonJ WorkManager interface itself, delegating all
|
||||
* calls to the target WorkManager. Hence, a caller can choose whether it wants
|
||||
* to talk to this executor through the Spring TaskExecutor interface or the
|
||||
* CommonJ WorkManager interface.
|
||||
*
|
||||
* <p>The CommonJ WorkManager will usually be retrieved from the application
|
||||
* server's JNDI environment, as defined in the server's management console.
|
||||
*
|
||||
* <p><b>Note: At the time of this writing, the CommonJ WorkManager facility
|
||||
* is only supported on IBM WebSphere 6.0+ and BEA WebLogic 9.0+,
|
||||
* despite being such a crucial API for an application server.</b>
|
||||
* (There is a similar facility available on WebSphere 5.1 Enterprise,
|
||||
* though, which we will discuss below.)
|
||||
*
|
||||
* <p><b>On JBoss and GlassFish, a similar facility is available through
|
||||
* the JCA WorkManager.</b> See the
|
||||
* {@link org.springframework.jca.work.jboss.JBossWorkManagerTaskExecutor}
|
||||
* {@link org.springframework.jca.work.glassfish.GlassFishWorkManagerTaskExecutor}
|
||||
* classes which are the direct equivalent of this CommonJ adapter class.
|
||||
*
|
||||
* <p>A similar facility is available on WebSphere 5.1, under the name
|
||||
* "Asynch Beans". Its central interface is called WorkManager too and is
|
||||
* also obtained from JNDI, just like a standard CommonJ WorkManager.
|
||||
* However, this WorkManager variant is notably different: The central
|
||||
* execution method is called "startWork" instead of "schedule",
|
||||
* and takes a slightly different Work interface as parameter.
|
||||
*
|
||||
* <p>Support for this WebSphere 5.1 variant can be built with this class
|
||||
* and its helper DelegatingWork as template: Call the WorkManager's
|
||||
* <code>startWork(Work)</code> instead of <code>schedule(Work)</code>
|
||||
* in the <code>execute(Runnable)</code> implementation. Furthermore,
|
||||
* for simplicity's sake, drop the entire "Implementation of the CommonJ
|
||||
* WorkManager interface" section (and the corresponding
|
||||
* <code>implements WorkManager</code> clause at the class level).
|
||||
* Of course, you also need to change all <code>commonj.work</code> imports in
|
||||
* your WorkManagerTaskExecutor and DelegatingWork variants to the corresponding
|
||||
* WebSphere API imports (<code>com.ibm.websphere.asynchbeans.WorkManager</code>
|
||||
* and <code>com.ibm.websphere.asynchbeans.Work</code>, respectively).
|
||||
* This should be sufficient to get a TaskExecutor adapter for WebSphere 5.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class WorkManagerTaskExecutor extends JndiLocatorSupport
|
||||
implements SchedulingTaskExecutor, WorkManager, InitializingBean {
|
||||
|
||||
private WorkManager workManager;
|
||||
|
||||
private String workManagerName;
|
||||
|
||||
private WorkListener workListener;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the CommonJ WorkManager to delegate to.
|
||||
* <p>Alternatively, you can also specify the JNDI name
|
||||
* of the target WorkManager.
|
||||
* @see #setWorkManagerName
|
||||
*/
|
||||
public void setWorkManager(WorkManager workManager) {
|
||||
this.workManager = workManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JNDI name of the CommonJ WorkManager.
|
||||
* <p>This can either be a fully qualified JNDI name,
|
||||
* or the JNDI name relative to the current environment
|
||||
* naming context if "resourceRef" is set to "true".
|
||||
* @see #setWorkManager
|
||||
* @see #setResourceRef
|
||||
*/
|
||||
public void setWorkManagerName(String workManagerName) {
|
||||
this.workManagerName = workManagerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a CommonJ WorkListener to apply, if any.
|
||||
* <p>This shared WorkListener instance will be passed on to the
|
||||
* WorkManager by all {@link #execute} calls on this TaskExecutor.
|
||||
*/
|
||||
public void setWorkListener(WorkListener workListener) {
|
||||
this.workListener = workListener;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
if (this.workManager == null) {
|
||||
if (this.workManagerName == null) {
|
||||
throw new IllegalArgumentException("Either 'workManager' or 'workManagerName' must be specified");
|
||||
}
|
||||
this.workManager = lookup(this.workManagerName, WorkManager.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Implementation of the Spring SchedulingTaskExecutor interface
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
public void execute(Runnable task) {
|
||||
Assert.state(this.workManager != null, "No WorkManager specified");
|
||||
Work work = new DelegatingWork(task);
|
||||
try {
|
||||
if (this.workListener != null) {
|
||||
this.workManager.schedule(work, this.workListener);
|
||||
}
|
||||
else {
|
||||
this.workManager.schedule(work);
|
||||
}
|
||||
}
|
||||
catch (WorkRejectedException ex) {
|
||||
throw new TaskRejectedException("CommonJ WorkManager did not accept task: " + task, ex);
|
||||
}
|
||||
catch (WorkException ex) {
|
||||
throw new SchedulingException("Could not schedule task on CommonJ WorkManager", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(Runnable task, long startTimeout) {
|
||||
execute(task);
|
||||
}
|
||||
|
||||
public Future<?> submit(Runnable task) {
|
||||
FutureTask<Object> future = new FutureTask<Object>(task, null);
|
||||
execute(future);
|
||||
return future;
|
||||
}
|
||||
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
FutureTask<T> future = new FutureTask<T>(task);
|
||||
execute(future);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* This task executor prefers short-lived work units.
|
||||
*/
|
||||
public boolean prefersShortLivedTasks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Implementation of the CommonJ WorkManager interface
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
public WorkItem schedule(Work work)
|
||||
throws WorkException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.schedule(work);
|
||||
}
|
||||
|
||||
public WorkItem schedule(Work work, WorkListener workListener)
|
||||
throws WorkException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.schedule(work, workListener);
|
||||
}
|
||||
|
||||
public boolean waitForAll(Collection workItems, long timeout)
|
||||
throws InterruptedException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.waitForAll(workItems, timeout);
|
||||
}
|
||||
|
||||
public Collection waitForAny(Collection workItems, long timeout)
|
||||
throws InterruptedException, IllegalArgumentException {
|
||||
|
||||
return this.workManager.waitForAny(workItems, timeout);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Convenience classes for scheduling based on the CommonJ WorkManager/TimerManager
|
||||
* facility, as supported by IBM WebSphere 6.0+ and BEA WebLogic 9.0+.
|
||||
*
|
||||
*/
|
||||
package org.springframework.scheduling.commonj;
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.spi.JobFactory;
|
||||
import org.quartz.spi.TriggerFiredBundle;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* JobFactory implementation that supports {@link java.lang.Runnable}
|
||||
* objects as well as standard Quartz {@link org.quartz.Job} instances.
|
||||
*
|
||||
* <p>Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see DelegatingJob
|
||||
* @see #adaptJob(Object)
|
||||
*/
|
||||
public class AdaptableJobFactory implements JobFactory {
|
||||
|
||||
/**
|
||||
* Quartz 2.0 version of newJob: simply delegates to old newJob variant.
|
||||
* @see #newJob(org.quartz.spi.TriggerFiredBundle)
|
||||
*/
|
||||
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
|
||||
return newJob(bundle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quartz 1.x version of newJob: contains actual implementation code.
|
||||
*/
|
||||
public Job newJob(TriggerFiredBundle bundle) throws SchedulerException {
|
||||
try {
|
||||
Object jobObject = createJobInstance(bundle);
|
||||
return adaptJob(jobObject);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new SchedulerException("Job instantiation failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the specified job class.
|
||||
* <p>Can be overridden to post-process the job instance.
|
||||
* @param bundle the TriggerFiredBundle from which the JobDetail
|
||||
* and other info relating to the trigger firing can be obtained
|
||||
* @return the job instance
|
||||
* @throws Exception if job instantiation failed
|
||||
*/
|
||||
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
|
||||
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
|
||||
Method getJobDetail = bundle.getClass().getMethod("getJobDetail");
|
||||
Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle);
|
||||
Method getJobClass = jobDetail.getClass().getMethod("getJobClass");
|
||||
Class jobClass = (Class) ReflectionUtils.invokeMethod(getJobClass, jobDetail);
|
||||
return jobClass.newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the given job object to the Quartz Job interface.
|
||||
* <p>The default implementation supports straight Quartz Jobs
|
||||
* as well as Runnables, which get wrapped in a DelegatingJob.
|
||||
* @param jobObject the original instance of the specified job class
|
||||
* @return the adapted Quartz Job instance
|
||||
* @throws Exception if the given job could not be adapted
|
||||
* @see DelegatingJob
|
||||
*/
|
||||
protected Job adaptJob(Object jobObject) throws Exception {
|
||||
if (jobObject instanceof Job) {
|
||||
return (Job) jobObject;
|
||||
}
|
||||
else if (jobObject instanceof Runnable) {
|
||||
return new DelegatingJob((Runnable) jobObject);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unable to execute job class [" + jobObject.getClass().getName() +
|
||||
"]: only [org.quartz.Job] and [java.lang.Runnable] supported.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.quartz.CronTrigger;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Convenience subclass of Quartz's {@link org.quartz.CronTrigger} class,
|
||||
* making bean-style usage easier.
|
||||
*
|
||||
* <p><code>CronTrigger</code> itself is already a JavaBean but lacks sensible defaults.
|
||||
* This class uses the Spring bean name as job name, the Quartz default group
|
||||
* ("DEFAULT") as job group, the current time as start time, and indefinite
|
||||
* repetition, if not specified.
|
||||
*
|
||||
* <p>This class will also register the trigger with the job name and group of
|
||||
* a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
|
||||
* to automatically register a trigger for the corresponding JobDetail,
|
||||
* instead of registering the JobDetail separately.
|
||||
*
|
||||
* <p><b>NOTE: This convenience subclass does not work against Quartz 2.0.</b>
|
||||
* Use Quartz 2.0's native <code>JobDetailImpl</code> class or the new Quartz 2.0
|
||||
* builder API instead. Alternatively, switch to Spring's {@link CronTriggerFactoryBean}
|
||||
* which largely is a drop-in replacement for this class and its properties and
|
||||
* consistently works against Quartz 1.x as well as Quartz 2.0/2.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setJobDetail
|
||||
* @see SchedulerFactoryBean#setTriggers
|
||||
* @see SchedulerFactoryBean#setJobDetails
|
||||
* @see SimpleTriggerBean
|
||||
*/
|
||||
public class CronTriggerBean extends CronTrigger
|
||||
implements JobDetailAwareTrigger, BeanNameAware, InitializingBean {
|
||||
|
||||
/** Constants for the CronTrigger class */
|
||||
private static final Constants constants = new Constants(CronTrigger.class);
|
||||
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private long startDelay;
|
||||
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Trigger only,
|
||||
* in contrast to objects in the JobDetail's data map.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
|
||||
getJobDataMap().putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the misfire instruction via the name of the corresponding
|
||||
* constant in the {@link org.quartz.CronTrigger} class.
|
||||
* Default is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>.
|
||||
* @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
|
||||
* @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
|
||||
* @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
|
||||
*/
|
||||
public void setMisfireInstructionName(String constantName) {
|
||||
setMisfireInstruction(constants.asNumber(constantName).intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of TriggerListener names for this job, referring to
|
||||
* non-global TriggerListeners registered with the Scheduler.
|
||||
* <p>A TriggerListener name always refers to the name returned
|
||||
* by the TriggerListener implementation.
|
||||
* @see SchedulerFactoryBean#setTriggerListeners
|
||||
* @see org.quartz.TriggerListener#getName
|
||||
*/
|
||||
public void setTriggerListenerNames(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
addTriggerListener(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the {@link #setStartTime start time} of the trigger.
|
||||
* <p>If the start delay is non-zero, it will <strong>always</strong>
|
||||
* take precedence over start time.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
Assert.state(startDelay >= 0, "Start delay cannot be negative.");
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JobDetail that this trigger should be associated with.
|
||||
* <p>This is typically used with a bean reference if the JobDetail
|
||||
* is a Spring-managed bean. Alternatively, the trigger can also
|
||||
* be associated with a job by name and group.
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
*/
|
||||
public void setJobDetail(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail;
|
||||
}
|
||||
|
||||
public JobDetail getJobDetail() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.startDelay > 0) {
|
||||
setStartTime(new Date(System.currentTimeMillis() + this.startDelay));
|
||||
}
|
||||
|
||||
if (getName() == null) {
|
||||
setName(this.beanName);
|
||||
}
|
||||
if (getGroup() == null) {
|
||||
setGroup(Scheduler.DEFAULT_GROUP);
|
||||
}
|
||||
if (getStartTime() == null) {
|
||||
setStartTime(new Date());
|
||||
}
|
||||
if (getTimeZone() == null) {
|
||||
setTimeZone(TimeZone.getDefault());
|
||||
}
|
||||
if (this.jobDetail != null) {
|
||||
setJobName(this.jobDetail.getName());
|
||||
setJobGroup(this.jobDetail.getGroup());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.quartz.CronTrigger;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.CronTrigger}
|
||||
* instance, supporting bean-style usage for trigger configuration.
|
||||
*
|
||||
* <p><code>CronTrigger(Impl)</code> itself is already a JavaBean but lacks sensible defaults.
|
||||
* This class uses the Spring bean name as job name, the Quartz default group ("DEFAULT")
|
||||
* as job group, the current time as start time, and indefinite repetition, if not specified.
|
||||
*
|
||||
* <p>This class will also register the trigger with the job name and group of
|
||||
* a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
|
||||
* to automatically register a trigger for the corresponding JobDetail,
|
||||
* instead of registering the JobDetail separately.
|
||||
*
|
||||
* <p><b>NOTE:</b> This FactoryBean works against both Quartz 1.x and Quartz 2.0/2.1,
|
||||
* in contrast to the older {@link CronTriggerBean} class.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setJobDetail
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
|
||||
* @see org.springframework.scheduling.quartz.SimpleTriggerBean
|
||||
*/
|
||||
public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNameAware, InitializingBean {
|
||||
|
||||
/** Constants for the CronTrigger class */
|
||||
private static final Constants constants = new Constants(CronTrigger.class);
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
private String group;
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
private JobDataMap jobDataMap = new JobDataMap();
|
||||
|
||||
private Date startTime;
|
||||
|
||||
private long startDelay;
|
||||
|
||||
private String cronExpression;
|
||||
|
||||
private TimeZone timeZone;
|
||||
|
||||
private int priority;
|
||||
|
||||
private int misfireInstruction;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private CronTrigger cronTrigger;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the trigger's name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the trigger's group.
|
||||
*/
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JobDetail that this trigger should be associated with.
|
||||
*/
|
||||
public void setJobDetail(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the trigger's JobDataMap.
|
||||
* @see #setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataMap(JobDataMap jobDataMap) {
|
||||
this.jobDataMap = jobDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the trigger's JobDataMap.
|
||||
*/
|
||||
public JobDataMap getJobDataMap() {
|
||||
return this.jobDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Trigger only,
|
||||
* in contrast to objects in the JobDetail's data map.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see org.springframework.scheduling.quartz.JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
|
||||
this.jobDataMap.putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the start time of the trigger.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
Assert.state(startDelay >= 0, "Start delay cannot be negative.");
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the cron expression for this trigger.
|
||||
*/
|
||||
public void setCronExpression(String cronExpression) {
|
||||
this.cronExpression = cronExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the time zone for this trigger's cron expression.
|
||||
*/
|
||||
public void setTimeZone(TimeZone timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the priority of this trigger.
|
||||
*/
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a misfire instruction for this trigger.
|
||||
*/
|
||||
public void setMisfireInstruction(int misfireInstruction) {
|
||||
this.misfireInstruction = misfireInstruction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the misfire instruction via the name of the corresponding
|
||||
* constant in the {@link org.quartz.CronTrigger} class.
|
||||
* Default is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>.
|
||||
* @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
|
||||
* @see org.quartz.CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
|
||||
* @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
|
||||
*/
|
||||
public void setMisfireInstructionName(String constantName) {
|
||||
this.misfireInstruction = constants.asNumber(constantName).intValue();
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (this.name == null) {
|
||||
this.name = this.beanName;
|
||||
}
|
||||
if (this.group == null) {
|
||||
this.group = Scheduler.DEFAULT_GROUP;
|
||||
}
|
||||
if (this.jobDetail != null) {
|
||||
this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail);
|
||||
}
|
||||
if (this.startDelay > 0) {
|
||||
this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
|
||||
}
|
||||
else if (this.startTime == null) {
|
||||
this.startTime = new Date();
|
||||
}
|
||||
if (this.timeZone == null) {
|
||||
this.timeZone = TimeZone.getDefault();
|
||||
}
|
||||
|
||||
/*
|
||||
CronTriggerImpl cti = new CronTriggerImpl();
|
||||
cti.setName(this.name);
|
||||
cti.setGroup(this.group);
|
||||
cti.setJobKey(this.jobDetail.getKey());
|
||||
cti.setJobDataMap(this.jobDataMap);
|
||||
cti.setStartTime(this.startTime);
|
||||
cti.setCronExpression(this.cronExpression);
|
||||
cti.setTimeZone(this.timeZone);
|
||||
cti.setPriority(this.priority);
|
||||
cti.setMisfireInstruction(this.misfireInstruction);
|
||||
this.cronTrigger = cti;
|
||||
*/
|
||||
|
||||
Class cronTriggerClass;
|
||||
Method jobKeyMethod;
|
||||
try {
|
||||
cronTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.CronTriggerImpl");
|
||||
jobKeyMethod = JobDetail.class.getMethod("getKey");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
cronTriggerClass = CronTrigger.class;
|
||||
jobKeyMethod = null;
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Incompatible Quartz version");
|
||||
}
|
||||
BeanWrapper bw = new BeanWrapperImpl(cronTriggerClass);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("name", this.name);
|
||||
pvs.add("group", this.group);
|
||||
if (jobKeyMethod != null) {
|
||||
pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail));
|
||||
}
|
||||
else {
|
||||
pvs.add("jobName", this.jobDetail.getName());
|
||||
pvs.add("jobGroup", this.jobDetail.getGroup());
|
||||
}
|
||||
pvs.add("jobDataMap", this.jobDataMap);
|
||||
pvs.add("startTime", this.startTime);
|
||||
pvs.add("cronExpression", this.cronExpression);
|
||||
pvs.add("timeZone", this.timeZone);
|
||||
pvs.add("priority", this.priority);
|
||||
pvs.add("misfireInstruction", this.misfireInstruction);
|
||||
bw.setPropertyValues(pvs);
|
||||
this.cronTrigger = (CronTrigger) bw.getWrappedInstance();
|
||||
}
|
||||
|
||||
|
||||
public CronTrigger getObject() {
|
||||
return this.cronTrigger;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return CronTrigger.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple Quartz {@link org.quartz.Job} adapter that delegates to a
|
||||
* given {@link java.lang.Runnable} instance.
|
||||
*
|
||||
* <p>Typically used in combination with property injection on the
|
||||
* Runnable instance, receiving parameters from the Quartz JobDataMap
|
||||
* that way instead of via the JobExecutionContext.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SpringBeanJobFactory
|
||||
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
|
||||
*/
|
||||
public class DelegatingJob implements Job {
|
||||
|
||||
private final Runnable delegate;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DelegatingJob.
|
||||
* @param delegate the Runnable implementation to delegate to
|
||||
*/
|
||||
public DelegatingJob(Runnable delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the wrapped Runnable implementation.
|
||||
*/
|
||||
public final Runnable getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates execution to the underlying Runnable.
|
||||
*/
|
||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
this.delegate.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.JobDetail;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by Quartz Triggers that are aware
|
||||
* of the JobDetail object that they are associated with.
|
||||
*
|
||||
* <p>SchedulerFactoryBean will auto-detect Triggers that implement this
|
||||
* interface and register them for the respective JobDetail accordingly.
|
||||
*
|
||||
* <p>The alternative is to configure a Trigger for a Job name and group:
|
||||
* This involves the need to register the JobDetail object separately
|
||||
* with SchedulerFactoryBean.
|
||||
*
|
||||
* <p><b>NOTE: As of Quartz 2.0, the recommended strategy is to define an
|
||||
* entry of name "jobDetail" and type JobDetail in the trigger's JobDataMap.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see SchedulerFactoryBean#setTriggers
|
||||
* @see SchedulerFactoryBean#setJobDetails
|
||||
* @see org.quartz.Trigger#setJobName
|
||||
* @see org.quartz.Trigger#setJobGroup
|
||||
*/
|
||||
public interface JobDetailAwareTrigger {
|
||||
|
||||
/**
|
||||
* Name of the key for the JobDetail value in the trigger's JobDataMap.
|
||||
* This is an alternative to implementing the JobDetailAwareTrigger interface.
|
||||
*/
|
||||
String JOB_DETAIL_KEY = "jobDetail";
|
||||
|
||||
/**
|
||||
* Return the JobDetail that this Trigger is associated with.
|
||||
* @return the associated JobDetail, or <code>null</code> if none
|
||||
*/
|
||||
JobDetail getJobDetail();
|
||||
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* Convenience subclass of Quartz's {@link org.quartz.JobDetail} class,
|
||||
* making bean-style usage easier.
|
||||
*
|
||||
* <p><code>JobDetail</code> itself is already a JavaBean but lacks
|
||||
* sensible defaults. This class uses the Spring bean name as job name,
|
||||
* and the Quartz default group ("DEFAULT") as job group if not specified.
|
||||
*
|
||||
* <p><b>NOTE: This convenience subclass does not work against Quartz 2.0.</b>
|
||||
* Use Quartz 2.0's native <code>JobDetailImpl</code> class or the new Quartz 2.0
|
||||
* builder API instead. Alternatively, switch to Spring's {@link JobDetailFactoryBean}
|
||||
* which largely is a drop-in replacement for this class and its properties and
|
||||
* consistently works against Quartz 1.x as well as Quartz 2.0/2.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see org.springframework.beans.factory.BeanNameAware
|
||||
* @see org.quartz.Scheduler#DEFAULT_GROUP
|
||||
*/
|
||||
public class JobDetailBean extends JobDetail
|
||||
implements BeanNameAware, ApplicationContextAware, InitializingBean {
|
||||
|
||||
private Class actualJobClass;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String applicationContextJobDataKey;
|
||||
|
||||
|
||||
/**
|
||||
* Overridden to support any job class, to allow a custom JobFactory
|
||||
* to adapt the given job class to the Quartz Job interface.
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
*/
|
||||
@Override
|
||||
public void setJobClass(Class jobClass) {
|
||||
if (jobClass != null && !Job.class.isAssignableFrom(jobClass)) {
|
||||
super.setJobClass(DelegatingJob.class);
|
||||
this.actualJobClass = jobClass;
|
||||
}
|
||||
else {
|
||||
super.setJobClass(jobClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to support any job class, to allow a custom JobFactory
|
||||
* to adapt the given job class to the Quartz Job interface.
|
||||
*/
|
||||
@Override
|
||||
public Class getJobClass() {
|
||||
return (this.actualJobClass != null ? this.actualJobClass : super.getJobClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Job only,
|
||||
* in contrast to objects in the SchedulerContext.
|
||||
* <p>Note: When using persistent Jobs whose JobDetail will be kept in the
|
||||
* database, do not put Spring-managed beans or an ApplicationContext
|
||||
* reference into the JobDataMap but rather into the SchedulerContext.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see SchedulerFactoryBean#setSchedulerContextAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map jobDataAsMap) {
|
||||
getJobDataMap().putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of JobListener names for this job, referring to
|
||||
* non-global JobListeners registered with the Scheduler.
|
||||
* <p>A JobListener name always refers to the name returned
|
||||
* by the JobListener implementation.
|
||||
* @see SchedulerFactoryBean#setJobListeners
|
||||
* @see org.quartz.JobListener#getName
|
||||
*/
|
||||
public void setJobListenerNames(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
addJobListener(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key of an ApplicationContext reference to expose in the JobDataMap,
|
||||
* for example "applicationContext". Default is none.
|
||||
* Only applicable when running in a Spring ApplicationContext.
|
||||
* <p>In case of a QuartzJobBean, the reference will be applied to the Job
|
||||
* instance as bean property. An "applicationContext" attribute will correspond
|
||||
* to a "setApplicationContext" method in that scenario.
|
||||
* <p>Note that BeanFactory callback interfaces like ApplicationContextAware
|
||||
* are not automatically applied to Quartz Job instances, because Quartz
|
||||
* itself is responsible for the lifecycle of its Jobs.
|
||||
* <p><b>Note: When using persistent job stores where JobDetail contents will
|
||||
* be kept in the database, do not put an ApplicationContext reference into
|
||||
* the JobDataMap but rather into the SchedulerContext.</b>
|
||||
* @see SchedulerFactoryBean#setApplicationContextSchedulerContextKey
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
public void setApplicationContextJobDataKey(String applicationContextJobDataKey) {
|
||||
this.applicationContextJobDataKey = applicationContextJobDataKey;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (getName() == null) {
|
||||
setName(this.beanName);
|
||||
}
|
||||
if (getGroup() == null) {
|
||||
setGroup(Scheduler.DEFAULT_GROUP);
|
||||
}
|
||||
if (this.applicationContextJobDataKey != null) {
|
||||
if (this.applicationContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"JobDetailBean needs to be set up in an ApplicationContext " +
|
||||
"to be able to handle an 'applicationContextJobDataKey'");
|
||||
}
|
||||
getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.JobDetail}
|
||||
* instance, supporting bean-style usage for JobDetail configuration.
|
||||
*
|
||||
* <p><code>JobDetail(Impl)</code> itself is already a JavaBean but lacks
|
||||
* sensible defaults. This class uses the Spring bean name as job name,
|
||||
* and the Quartz default group ("DEFAULT") as job group if not specified.
|
||||
*
|
||||
* <p><b>NOTE:</b> This FactoryBean works against both Quartz 1.x and Quartz 2.0/2.1,
|
||||
* in contrast to the older {@link JobDetailBean} class.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see org.springframework.beans.factory.BeanNameAware
|
||||
* @see org.quartz.Scheduler#DEFAULT_GROUP
|
||||
*/
|
||||
public class JobDetailFactoryBean
|
||||
implements FactoryBean<JobDetail>, BeanNameAware, ApplicationContextAware, InitializingBean {
|
||||
|
||||
private String name;
|
||||
|
||||
private String group;
|
||||
|
||||
private Class jobClass;
|
||||
|
||||
private JobDataMap jobDataMap = new JobDataMap();
|
||||
|
||||
private String beanName;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String applicationContextJobDataKey;
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the job's name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the job's group.
|
||||
*/
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the job's implementation class.
|
||||
*/
|
||||
public void setJobClass(Class jobClass) {
|
||||
this.jobClass = jobClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the job's JobDataMap.
|
||||
* @see #setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataMap(JobDataMap jobDataMap) {
|
||||
this.jobDataMap = jobDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the job's JobDataMap.
|
||||
*/
|
||||
public JobDataMap getJobDataMap() {
|
||||
return this.jobDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Job only,
|
||||
* in contrast to objects in the SchedulerContext.
|
||||
* <p>Note: When using persistent Jobs whose JobDetail will be kept in the
|
||||
* database, do not put Spring-managed beans or an ApplicationContext
|
||||
* reference into the JobDataMap but rather into the SchedulerContext.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setSchedulerContextAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
|
||||
getJobDataMap().putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key of an ApplicationContext reference to expose in the JobDataMap,
|
||||
* for example "applicationContext". Default is none.
|
||||
* Only applicable when running in a Spring ApplicationContext.
|
||||
* <p>In case of a QuartzJobBean, the reference will be applied to the Job
|
||||
* instance as bean property. An "applicationContext" attribute will correspond
|
||||
* to a "setApplicationContext" method in that scenario.
|
||||
* <p>Note that BeanFactory callback interfaces like ApplicationContextAware
|
||||
* are not automatically applied to Quartz Job instances, because Quartz
|
||||
* itself is responsible for the lifecycle of its Jobs.
|
||||
* <p><b>Note: When using persistent job stores where JobDetail contents will
|
||||
* be kept in the database, do not put an ApplicationContext reference into
|
||||
* the JobDataMap but rather into the SchedulerContext.</b>
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setApplicationContextSchedulerContextKey
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
public void setApplicationContextJobDataKey(String applicationContextJobDataKey) {
|
||||
this.applicationContextJobDataKey = applicationContextJobDataKey;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterPropertiesSet() {
|
||||
if (this.name == null) {
|
||||
this.name = this.beanName;
|
||||
}
|
||||
if (this.group == null) {
|
||||
this.group = Scheduler.DEFAULT_GROUP;
|
||||
}
|
||||
if (this.applicationContextJobDataKey != null) {
|
||||
if (this.applicationContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"JobDetailBean needs to be set up in an ApplicationContext " +
|
||||
"to be able to handle an 'applicationContextJobDataKey'");
|
||||
}
|
||||
getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
|
||||
}
|
||||
|
||||
/*
|
||||
JobDetailImpl jdi = new JobDetailImpl();
|
||||
jdi.setName(this.name);
|
||||
jdi.setGroup(this.group);
|
||||
jdi.setJobClass(this.jobClass);
|
||||
jdi.setJobDataMap(this.jobDataMap);
|
||||
this.jobDetail = jdi;
|
||||
*/
|
||||
|
||||
Class jobDetailClass;
|
||||
try {
|
||||
jobDetailClass = getClass().getClassLoader().loadClass("org.quartz.impl.JobDetailImpl");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
jobDetailClass = JobDetail.class;
|
||||
}
|
||||
BeanWrapper bw = new BeanWrapperImpl(jobDetailClass);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("name", this.name);
|
||||
pvs.add("group", this.group);
|
||||
pvs.add("jobClass", this.jobClass);
|
||||
pvs.add("jobDataMap", this.jobDataMap);
|
||||
bw.setPropertyValues(pvs);
|
||||
this.jobDetail = (JobDetail) bw.getWrappedInstance();
|
||||
}
|
||||
|
||||
|
||||
public JobDetail getObject() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return JobDetail.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* Unchecked exception that wraps an exception thrown from a target method.
|
||||
* Propagated to the Quartz scheduler from a Job that reflectively invokes
|
||||
* an arbitrary target method.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.3
|
||||
* @see MethodInvokingJobDetailFactoryBean
|
||||
*/
|
||||
public class JobMethodInvocationFailedException extends NestedRuntimeException {
|
||||
|
||||
/**
|
||||
* Constructor for JobMethodInvocationFailedException.
|
||||
* @param methodInvoker the MethodInvoker used for reflective invocation
|
||||
* @param cause the root cause (as thrown from the target method)
|
||||
*/
|
||||
public JobMethodInvocationFailedException(MethodInvoker methodInvoker, Throwable cause) {
|
||||
super("Invocation of method '" + methodInvoker.getTargetMethod() +
|
||||
"' on target class [" + methodInvoker.getTargetClass() + "] failed", cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.quartz.SchedulerConfigException;
|
||||
import org.quartz.impl.jdbcjobstore.JobStoreCMT;
|
||||
import org.quartz.impl.jdbcjobstore.SimpleSemaphore;
|
||||
import org.quartz.spi.ClassLoadHelper;
|
||||
import org.quartz.spi.SchedulerSignaler;
|
||||
import org.quartz.utils.ConnectionProvider;
|
||||
import org.quartz.utils.DBConnectionManager;
|
||||
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.jdbc.support.MetaDataAccessException;
|
||||
|
||||
/**
|
||||
* Subclass of Quartz's JobStoreCMT class that delegates to a Spring-managed
|
||||
* DataSource instead of using a Quartz-managed connection pool. This JobStore
|
||||
* will be used if SchedulerFactoryBean's "dataSource" property is set.
|
||||
*
|
||||
* <p>Supports both transactional and non-transactional DataSource access.
|
||||
* With a non-XA DataSource and local Spring transactions, a single DataSource
|
||||
* argument is sufficient. In case of an XA DataSource and global JTA transactions,
|
||||
* SchedulerFactoryBean's "nonTransactionalDataSource" property should be set,
|
||||
* passing in a non-XA DataSource that will not participate in global transactions.
|
||||
*
|
||||
* <p>Operations performed by this JobStore will properly participate in any
|
||||
* kind of Spring-managed transaction, as it uses Spring's DataSourceUtils
|
||||
* connection handling methods that are aware of a current transaction.
|
||||
*
|
||||
* <p>Note that all Quartz Scheduler operations that affect the persistent
|
||||
* job store should usually be performed within active transactions,
|
||||
* as they assume to get proper locks etc.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
* @see SchedulerFactoryBean#setNonTransactionalDataSource
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
|
||||
*/
|
||||
public class LocalDataSourceJobStore extends JobStoreCMT {
|
||||
|
||||
/**
|
||||
* Name used for the transactional ConnectionProvider for Quartz.
|
||||
* This provider will delegate to the local Spring-managed DataSource.
|
||||
* @see org.quartz.utils.DBConnectionManager#addConnectionProvider
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
*/
|
||||
public static final String TX_DATA_SOURCE_PREFIX = "springTxDataSource.";
|
||||
|
||||
/**
|
||||
* Name used for the non-transactional ConnectionProvider for Quartz.
|
||||
* This provider will delegate to the local Spring-managed DataSource.
|
||||
* @see org.quartz.utils.DBConnectionManager#addConnectionProvider
|
||||
* @see SchedulerFactoryBean#setDataSource
|
||||
*/
|
||||
public static final String NON_TX_DATA_SOURCE_PREFIX = "springNonTxDataSource.";
|
||||
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler)
|
||||
throws SchedulerConfigException {
|
||||
|
||||
// Absolutely needs thread-bound DataSource to initialize.
|
||||
this.dataSource = SchedulerFactoryBean.getConfigTimeDataSource();
|
||||
if (this.dataSource == null) {
|
||||
throw new SchedulerConfigException(
|
||||
"No local DataSource found for configuration - " +
|
||||
"'dataSource' property must be set on SchedulerFactoryBean");
|
||||
}
|
||||
|
||||
// Configure transactional connection settings for Quartz.
|
||||
setDataSource(TX_DATA_SOURCE_PREFIX + getInstanceName());
|
||||
setDontSetAutoCommitFalse(true);
|
||||
|
||||
// Register transactional ConnectionProvider for Quartz.
|
||||
DBConnectionManager.getInstance().addConnectionProvider(
|
||||
TX_DATA_SOURCE_PREFIX + getInstanceName(),
|
||||
new ConnectionProvider() {
|
||||
public Connection getConnection() throws SQLException {
|
||||
// Return a transactional Connection, if any.
|
||||
return DataSourceUtils.doGetConnection(dataSource);
|
||||
}
|
||||
public void shutdown() {
|
||||
// Do nothing - a Spring-managed DataSource has its own lifecycle.
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Non-transactional DataSource is optional: fall back to default
|
||||
// DataSource if not explicitly specified.
|
||||
DataSource nonTxDataSource = SchedulerFactoryBean.getConfigTimeNonTransactionalDataSource();
|
||||
final DataSource nonTxDataSourceToUse =
|
||||
(nonTxDataSource != null ? nonTxDataSource : this.dataSource);
|
||||
|
||||
// Configure non-transactional connection settings for Quartz.
|
||||
setNonManagedTXDataSource(NON_TX_DATA_SOURCE_PREFIX + getInstanceName());
|
||||
|
||||
// Register non-transactional ConnectionProvider for Quartz.
|
||||
DBConnectionManager.getInstance().addConnectionProvider(
|
||||
NON_TX_DATA_SOURCE_PREFIX + getInstanceName(),
|
||||
new ConnectionProvider() {
|
||||
public Connection getConnection() throws SQLException {
|
||||
// Always return a non-transactional Connection.
|
||||
return nonTxDataSourceToUse.getConnection();
|
||||
}
|
||||
public void shutdown() {
|
||||
// Do nothing - a Spring-managed DataSource has its own lifecycle.
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// No, if HSQL is the platform, we really don't want to use locks
|
||||
try {
|
||||
String productName = JdbcUtils.extractDatabaseMetaData(dataSource,
|
||||
"getDatabaseProductName").toString();
|
||||
productName = JdbcUtils.commonDatabaseName(productName);
|
||||
if (productName != null
|
||||
&& productName.toLowerCase().contains("hsql")) {
|
||||
setUseDBLocks(false);
|
||||
setLockHandler(new SimpleSemaphore());
|
||||
}
|
||||
} catch (MetaDataAccessException e) {
|
||||
logWarnIfNonZero(1, "Could not detect database type. Assuming locks can be taken.");
|
||||
}
|
||||
|
||||
super.initialize(loadHelper, signaler);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void closeConnection(Connection con) {
|
||||
// Will work for transactional and non-transactional connections.
|
||||
DataSourceUtils.releaseConnection(con, this.dataSource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.SchedulerConfigException;
|
||||
import org.quartz.spi.ThreadPool;
|
||||
|
||||
/**
|
||||
* Quartz ThreadPool adapter that delegates to a Spring-managed
|
||||
* TaskExecutor instance, specified on SchedulerFactoryBean.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SchedulerFactoryBean#setTaskExecutor
|
||||
*/
|
||||
public class LocalTaskExecutorThreadPool implements ThreadPool {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Executor taskExecutor;
|
||||
|
||||
|
||||
public void setInstanceId(String schedInstId) {
|
||||
}
|
||||
|
||||
public void setInstanceName(String schedName) {
|
||||
}
|
||||
|
||||
|
||||
public void initialize() throws SchedulerConfigException {
|
||||
// Absolutely needs thread-bound TaskExecutor to initialize.
|
||||
this.taskExecutor = SchedulerFactoryBean.getConfigTimeTaskExecutor();
|
||||
if (this.taskExecutor == null) {
|
||||
throw new SchedulerConfigException(
|
||||
"No local TaskExecutor found for configuration - " +
|
||||
"'taskExecutor' property must be set on SchedulerFactoryBean");
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown(boolean waitForJobsToComplete) {
|
||||
}
|
||||
|
||||
public int getPoolSize() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
public boolean runInThread(Runnable runnable) {
|
||||
if (runnable == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
this.taskExecutor.execute(runnable);
|
||||
return true;
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
logger.error("Task has been rejected by TaskExecutor", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int blockForAvailableThreads() {
|
||||
// The present implementation always returns 1, making Quartz (1.6)
|
||||
// always schedule any tasks that it feels like scheduling.
|
||||
// This could be made smarter for specific TaskExecutors,
|
||||
// for example calling <code>getMaximumPoolSize() - getActiveCount()</code>
|
||||
// on a <code>java.util.concurrent.ThreadPoolExecutor</code>.
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.StatefulJob;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.support.ArgumentConvertingMethodInvoker;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.FactoryBean} that exposes a
|
||||
* {@link org.quartz.JobDetail} object which delegates job execution to a
|
||||
* specified (static or non-static) method. Avoids the need for implementing
|
||||
* a one-line Quartz Job that just invokes an existing service method on a
|
||||
* Spring-managed target bean.
|
||||
*
|
||||
* <p>Inherits common configuration properties from the {@link MethodInvoker}
|
||||
* base class, such as {@link #setTargetObject "targetObject"} and
|
||||
* {@link #setTargetMethod "targetMethod"}, adding support for lookup of the target
|
||||
* bean by name through the {@link #setTargetBeanName "targetBeanName"} property
|
||||
* (as alternative to specifying a "targetObject" directly, allowing for
|
||||
* non-singleton target objects).
|
||||
*
|
||||
* <p>Supports both concurrently running jobs and non-currently running
|
||||
* jobs through the "concurrent" property. Jobs created by this
|
||||
* MethodInvokingJobDetailFactoryBean are by default volatile and durable
|
||||
* (according to Quartz terminology).
|
||||
*
|
||||
* <p><b>NOTE: JobDetails created via this FactoryBean are <i>not</i>
|
||||
* serializable and thus not suitable for persistent job stores.</b>
|
||||
* You need to implement your own Quartz Job as a thin wrapper for each case
|
||||
* where you want a persistent job to delegate to a specific service method.
|
||||
*
|
||||
* <p>Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Alef Arendsen
|
||||
* @since 18.02.2004
|
||||
* @see #setTargetBeanName
|
||||
* @see #setTargetObject
|
||||
* @see #setTargetMethod
|
||||
* @see #setConcurrent
|
||||
*/
|
||||
public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethodInvoker
|
||||
implements FactoryBean<JobDetail>, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private static Class<?> jobDetailImplClass;
|
||||
|
||||
static {
|
||||
try {
|
||||
jobDetailImplClass = Class.forName("org.quartz.impl.JobDetailImpl");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
jobDetailImplClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
private String group = Scheduler.DEFAULT_GROUP;
|
||||
|
||||
private boolean concurrent = true;
|
||||
|
||||
private String targetBeanName;
|
||||
|
||||
private String[] jobListenerNames;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
|
||||
/**
|
||||
* Set the name of the job.
|
||||
* <p>Default is the bean name of this FactoryBean.
|
||||
* @see org.quartz.JobDetail#setName
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the group of the job.
|
||||
* <p>Default is the default group of the Scheduler.
|
||||
* @see org.quartz.JobDetail#setGroup
|
||||
* @see org.quartz.Scheduler#DEFAULT_GROUP
|
||||
*/
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether or not multiple jobs should be run in a concurrent
|
||||
* fashion. The behavior when one does not want concurrent jobs to be
|
||||
* executed is realized through adding the {@link StatefulJob} interface.
|
||||
* More information on stateful versus stateless jobs can be found
|
||||
* <a href="http://www.opensymphony.com/quartz/tutorial.html#jobsMore">here</a>.
|
||||
* <p>The default setting is to run jobs concurrently.
|
||||
*/
|
||||
public void setConcurrent(boolean concurrent) {
|
||||
this.concurrent = concurrent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the target bean in the Spring BeanFactory.
|
||||
* <p>This is an alternative to specifying {@link #setTargetObject "targetObject"},
|
||||
* allowing for non-singleton beans to be invoked. Note that specified
|
||||
* "targetObject" and {@link #setTargetClass "targetClass"} values will
|
||||
* override the corresponding effect of this "targetBeanName" setting
|
||||
* (i.e. statically pre-define the bean type or even the bean object).
|
||||
*/
|
||||
public void setTargetBeanName(String targetBeanName) {
|
||||
this.targetBeanName = targetBeanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of JobListener names for this job, referring to
|
||||
* non-global JobListeners registered with the Scheduler.
|
||||
* <p>A JobListener name always refers to the name returned
|
||||
* by the JobListener implementation.
|
||||
* @see SchedulerFactoryBean#setJobListeners
|
||||
* @see org.quartz.JobListener#getName
|
||||
*/
|
||||
public void setJobListenerNames(String[] names) {
|
||||
this.jobListenerNames = names;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class resolveClassName(String className) throws ClassNotFoundException {
|
||||
return ClassUtils.forName(className, this.beanClassLoader);
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {
|
||||
prepare();
|
||||
|
||||
// Use specific name if given, else fall back to bean name.
|
||||
String name = (this.name != null ? this.name : this.beanName);
|
||||
|
||||
// Consider the concurrent flag to choose between stateful and stateless job.
|
||||
Class jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);
|
||||
|
||||
// Build JobDetail instance.
|
||||
if (jobDetailImplClass != null) {
|
||||
// Using Quartz 2.0 JobDetailImpl class...
|
||||
this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass);
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this.jobDetail);
|
||||
bw.setPropertyValue("name", name);
|
||||
bw.setPropertyValue("group", this.group);
|
||||
bw.setPropertyValue("jobClass", jobClass);
|
||||
bw.setPropertyValue("durability", true);
|
||||
((JobDataMap) bw.getPropertyValue("jobDataMap")).put("methodInvoker", this);
|
||||
}
|
||||
else {
|
||||
// Using Quartz 1.x JobDetail class...
|
||||
this.jobDetail = new JobDetail(name, this.group, jobClass);
|
||||
this.jobDetail.setVolatility(true);
|
||||
this.jobDetail.setDurability(true);
|
||||
this.jobDetail.getJobDataMap().put("methodInvoker", this);
|
||||
}
|
||||
|
||||
// Register job listener names.
|
||||
if (this.jobListenerNames != null) {
|
||||
for (String jobListenerName : this.jobListenerNames) {
|
||||
if (jobDetailImplClass != null) {
|
||||
throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " +
|
||||
"manually register a Matcher against the Quartz ListenerManager instead");
|
||||
}
|
||||
this.jobDetail.addJobListener(jobListenerName);
|
||||
}
|
||||
}
|
||||
|
||||
postProcessJobDetail(this.jobDetail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for post-processing the JobDetail to be exposed by this FactoryBean.
|
||||
* <p>The default implementation is empty. Can be overridden in subclasses.
|
||||
* @param jobDetail the JobDetail prepared by this FactoryBean
|
||||
*/
|
||||
protected void postProcessJobDetail(JobDetail jobDetail) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
|
||||
*/
|
||||
@Override
|
||||
public Class getTargetClass() {
|
||||
Class targetClass = super.getTargetClass();
|
||||
if (targetClass == null && this.targetBeanName != null) {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory must be set when using 'targetBeanName'");
|
||||
targetClass = this.beanFactory.getType(this.targetBeanName);
|
||||
}
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to support the {@link #setTargetBeanName "targetBeanName"} feature.
|
||||
*/
|
||||
@Override
|
||||
public Object getTargetObject() {
|
||||
Object targetObject = super.getTargetObject();
|
||||
if (targetObject == null && this.targetBeanName != null) {
|
||||
Assert.state(this.beanFactory != null, "BeanFactory must be set when using 'targetBeanName'");
|
||||
targetObject = this.beanFactory.getBean(this.targetBeanName);
|
||||
}
|
||||
return targetObject;
|
||||
}
|
||||
|
||||
|
||||
public JobDetail getObject() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
public Class<? extends JobDetail> getObjectType() {
|
||||
return (this.jobDetail != null ? this.jobDetail.getClass() : JobDetail.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quartz Job implementation that invokes a specified method.
|
||||
* Automatically applied by MethodInvokingJobDetailFactoryBean.
|
||||
*/
|
||||
public static class MethodInvokingJob extends QuartzJobBean {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(MethodInvokingJob.class);
|
||||
|
||||
private MethodInvoker methodInvoker;
|
||||
|
||||
/**
|
||||
* Set the MethodInvoker to use.
|
||||
*/
|
||||
public void setMethodInvoker(MethodInvoker methodInvoker) {
|
||||
this.methodInvoker = methodInvoker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the method via the MethodInvoker.
|
||||
*/
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
|
||||
try {
|
||||
context.setResult(this.methodInvoker.invoke());
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
if (ex.getTargetException() instanceof JobExecutionException) {
|
||||
// -> JobExecutionException, to be logged at info level by Quartz
|
||||
throw (JobExecutionException) ex.getTargetException();
|
||||
}
|
||||
else {
|
||||
// -> "unhandled exception", to be logged at error level by Quartz
|
||||
throw new JobMethodInvocationFailedException(this.methodInvoker, ex.getTargetException());
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// -> "unhandled exception", to be logged at error level by Quartz
|
||||
throw new JobMethodInvocationFailedException(this.methodInvoker, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extension of the MethodInvokingJob, implementing the StatefulJob interface.
|
||||
* Quartz checks whether or not jobs are stateful and if so,
|
||||
* won't let jobs interfere with each other.
|
||||
*/
|
||||
public static class StatefulMethodInvokingJob extends MethodInvokingJob implements StatefulJob {
|
||||
|
||||
// No implementation, just an addition of the tag interface StatefulJob
|
||||
// in order to allow stateful method invoking jobs.
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Simple implementation of the Quartz Job interface, applying the
|
||||
* passed-in JobDataMap and also the SchedulerContext as bean property
|
||||
* values. This is appropriate because a new Job instance will be created
|
||||
* for each execution. JobDataMap entries will override SchedulerContext
|
||||
* entries with the same keys.
|
||||
*
|
||||
* <p>For example, let's assume that the JobDataMap contains a key
|
||||
* "myParam" with value "5": The Job implementation can then expose
|
||||
* a bean property "myParam" of type int to receive such a value,
|
||||
* i.e. a method "setMyParam(int)". This will also work for complex
|
||||
* types like business objects etc.
|
||||
*
|
||||
* <p>Note: The QuartzJobBean class itself only implements the standard
|
||||
* Quartz {@link org.quartz.Job} interface. Let your subclass explicitly
|
||||
* implement the Quartz {@link org.quartz.StatefulJob} interface to
|
||||
* mark your concrete job bean as stateful.
|
||||
*
|
||||
* <p>This version of QuartzJobBean requires Quartz 1.5 or higher,
|
||||
* due to the support for trigger-specific job data.
|
||||
*
|
||||
* <p><b>Note that as of Spring 2.0 and Quartz 1.5, the preferred way
|
||||
* to apply dependency injection to Job instances is via a JobFactory:</b>
|
||||
* that is, to specify {@link SpringBeanJobFactory} as Quartz JobFactory
|
||||
* (typically via
|
||||
* {@link SchedulerFactoryBean#setJobFactory} SchedulerFactoryBean's "jobFactory" property}).
|
||||
* This allows to implement dependency-injected Quartz Jobs without
|
||||
* a dependency on Spring base classes.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see org.quartz.JobExecutionContext#getMergedJobDataMap()
|
||||
* @see org.quartz.Scheduler#getContext()
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
* @see SimpleTriggerBean#setJobDataAsMap
|
||||
* @see CronTriggerBean#setJobDataAsMap
|
||||
* @see SchedulerFactoryBean#setSchedulerContextAsMap
|
||||
* @see SpringBeanJobFactory
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
*/
|
||||
public abstract class QuartzJobBean implements Job {
|
||||
|
||||
private static final Method getSchedulerMethod;
|
||||
|
||||
private static final Method getMergedJobDataMapMethod;
|
||||
|
||||
|
||||
static {
|
||||
try {
|
||||
Class jobExecutionContextClass =
|
||||
QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext");
|
||||
getSchedulerMethod = jobExecutionContextClass.getMethod("getScheduler");
|
||||
getMergedJobDataMapMethod = jobExecutionContextClass.getMethod("getMergedJobDataMap");
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Incompatible Quartz API: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation applies the passed-in job data map as bean property
|
||||
* values, and delegates to <code>executeInternal</code> afterwards.
|
||||
* @see #executeInternal
|
||||
*/
|
||||
public final void execute(JobExecutionContext context) throws JobExecutionException {
|
||||
try {
|
||||
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
|
||||
Scheduler scheduler = (Scheduler) ReflectionUtils.invokeMethod(getSchedulerMethod, context);
|
||||
Map mergedJobDataMap = (Map) ReflectionUtils.invokeMethod(getMergedJobDataMapMethod, context);
|
||||
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.addPropertyValues(scheduler.getContext());
|
||||
pvs.addPropertyValues(mergedJobDataMap);
|
||||
bw.setPropertyValues(pvs, true);
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new JobExecutionException(ex);
|
||||
}
|
||||
executeInternal(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the actual job. The job data map will already have been
|
||||
* applied as bean property values by execute. The contract is
|
||||
* exactly the same as for the standard Quartz execute method.
|
||||
* @see #execute
|
||||
*/
|
||||
protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;
|
||||
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.spi.ClassLoadHelper;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Wrapper that adapts from the Quartz {@link ClassLoadHelper} interface
|
||||
* onto Spring's {@link ResourceLoader} interface. Used by default when
|
||||
* the SchedulerFactoryBean runs in a Spring ApplicationContext.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.5
|
||||
* @see SchedulerFactoryBean#setApplicationContext
|
||||
*/
|
||||
public class ResourceLoaderClassLoadHelper implements ClassLoadHelper {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(ResourceLoaderClassLoadHelper.class);
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ResourceLoaderClassLoadHelper for the default
|
||||
* ResourceLoader.
|
||||
* @see SchedulerFactoryBean#getConfigTimeResourceLoader()
|
||||
*/
|
||||
public ResourceLoaderClassLoadHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ResourceLoaderClassLoadHelper for the given ResourceLoader.
|
||||
* @param resourceLoader the ResourceLoader to delegate to
|
||||
*/
|
||||
public ResourceLoaderClassLoadHelper(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
|
||||
public void initialize() {
|
||||
if (this.resourceLoader == null) {
|
||||
this.resourceLoader = SchedulerFactoryBean.getConfigTimeResourceLoader();
|
||||
if (this.resourceLoader == null) {
|
||||
this.resourceLoader = new DefaultResourceLoader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Class loadClass(String name) throws ClassNotFoundException {
|
||||
return this.resourceLoader.getClassLoader().loadClass(name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Class<? extends T> loadClass(String name, Class<T> clazz) throws ClassNotFoundException {
|
||||
return loadClass(name);
|
||||
}
|
||||
|
||||
public URL getResource(String name) {
|
||||
Resource resource = this.resourceLoader.getResource(name);
|
||||
try {
|
||||
return resource.getURL();
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Could not load " + resource);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
Resource resource = this.resourceLoader.getResource(name);
|
||||
try {
|
||||
return resource.getInputStream();
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.warn("Could not load " + resource);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.resourceLoader.getClassLoader();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,529 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.quartz.Calendar;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.JobListener;
|
||||
import org.quartz.ObjectAlreadyExistsException;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.SchedulerListener;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.TriggerListener;
|
||||
import org.quartz.spi.ClassLoadHelper;
|
||||
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.DefaultTransactionDefinition;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Common base class for accessing a Quartz Scheduler, i.e. for registering jobs,
|
||||
* triggers and listeners on a {@link org.quartz.Scheduler} instance.
|
||||
*
|
||||
* <p>For concrete usage, check out the {@link SchedulerFactoryBean} and
|
||||
* {@link SchedulerAccessorBean} classes.
|
||||
*
|
||||
* <p>Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.6
|
||||
*/
|
||||
public abstract class SchedulerAccessor implements ResourceLoaderAware {
|
||||
|
||||
private static Class<?> jobKeyClass;
|
||||
|
||||
private static Class<?> triggerKeyClass;
|
||||
|
||||
static {
|
||||
try {
|
||||
jobKeyClass = Class.forName("org.quartz.JobKey");
|
||||
triggerKeyClass = Class.forName("org.quartz.TriggerKey");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
jobKeyClass = null;
|
||||
triggerKeyClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean overwriteExistingJobs = false;
|
||||
|
||||
private String[] jobSchedulingDataLocations;
|
||||
|
||||
private List<JobDetail> jobDetails;
|
||||
|
||||
private Map<String, Calendar> calendars;
|
||||
|
||||
private List<Trigger> triggers;
|
||||
|
||||
private SchedulerListener[] schedulerListeners;
|
||||
|
||||
private JobListener[] globalJobListeners;
|
||||
|
||||
private JobListener[] jobListeners;
|
||||
|
||||
private TriggerListener[] globalTriggerListeners;
|
||||
|
||||
private TriggerListener[] triggerListeners;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
protected ResourceLoader resourceLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether any jobs defined on this SchedulerFactoryBean should overwrite
|
||||
* existing job definitions. Default is "false", to not overwrite already
|
||||
* registered jobs that have been read in from a persistent job store.
|
||||
*/
|
||||
public void setOverwriteExistingJobs(boolean overwriteExistingJobs) {
|
||||
this.overwriteExistingJobs = overwriteExistingJobs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location of a Quartz job definition XML file that follows the
|
||||
* "job_scheduling_data_1_5" XSD. Can be specified to automatically
|
||||
* register jobs that are defined in such a file, possibly in addition
|
||||
* to jobs defined directly on this SchedulerFactoryBean.
|
||||
* @see org.quartz.xml.JobSchedulingDataProcessor
|
||||
*/
|
||||
public void setJobSchedulingDataLocation(String jobSchedulingDataLocation) {
|
||||
this.jobSchedulingDataLocations = new String[] {jobSchedulingDataLocation};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locations of Quartz job definition XML files that follow the
|
||||
* "job_scheduling_data_1_5" XSD. Can be specified to automatically
|
||||
* register jobs that are defined in such files, possibly in addition
|
||||
* to jobs defined directly on this SchedulerFactoryBean.
|
||||
* @see org.quartz.xml.JobSchedulingDataProcessor
|
||||
*/
|
||||
public void setJobSchedulingDataLocations(String[] jobSchedulingDataLocations) {
|
||||
this.jobSchedulingDataLocations = jobSchedulingDataLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a list of JobDetail objects with the Scheduler that
|
||||
* this FactoryBean creates, to be referenced by Triggers.
|
||||
* <p>This is not necessary when a Trigger determines the JobDetail
|
||||
* itself: In this case, the JobDetail will be implicitly registered
|
||||
* in combination with the Trigger.
|
||||
* @see #setTriggers
|
||||
* @see org.quartz.JobDetail
|
||||
* @see JobDetailBean
|
||||
* @see JobDetailAwareTrigger
|
||||
* @see org.quartz.Trigger#setJobName
|
||||
*/
|
||||
public void setJobDetails(JobDetail[] jobDetails) {
|
||||
// Use modifiable ArrayList here, to allow for further adding of
|
||||
// JobDetail objects during autodetection of JobDetailAwareTriggers.
|
||||
this.jobDetails = new ArrayList<JobDetail>(Arrays.asList(jobDetails));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a list of Quartz Calendar objects with the Scheduler
|
||||
* that this FactoryBean creates, to be referenced by Triggers.
|
||||
* @param calendars Map with calendar names as keys as Calendar
|
||||
* objects as values
|
||||
* @see org.quartz.Calendar
|
||||
* @see org.quartz.Trigger#setCalendarName
|
||||
*/
|
||||
public void setCalendars(Map<String, Calendar> calendars) {
|
||||
this.calendars = calendars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a list of Trigger objects with the Scheduler that
|
||||
* this FactoryBean creates.
|
||||
* <p>If the Trigger determines the corresponding JobDetail itself,
|
||||
* the job will be automatically registered with the Scheduler.
|
||||
* Else, the respective JobDetail needs to be registered via the
|
||||
* "jobDetails" property of this FactoryBean.
|
||||
* @see #setJobDetails
|
||||
* @see org.quartz.JobDetail
|
||||
* @see JobDetailAwareTrigger
|
||||
* @see CronTriggerBean
|
||||
* @see SimpleTriggerBean
|
||||
*/
|
||||
public void setTriggers(Trigger[] triggers) {
|
||||
this.triggers = Arrays.asList(triggers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify Quartz SchedulerListeners to be registered with the Scheduler.
|
||||
*/
|
||||
public void setSchedulerListeners(SchedulerListener[] schedulerListeners) {
|
||||
this.schedulerListeners = schedulerListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify global Quartz JobListeners to be registered with the Scheduler.
|
||||
* Such JobListeners will apply to all Jobs in the Scheduler.
|
||||
*/
|
||||
public void setGlobalJobListeners(JobListener[] globalJobListeners) {
|
||||
this.globalJobListeners = globalJobListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify named Quartz JobListeners to be registered with the Scheduler.
|
||||
* Such JobListeners will only apply to Jobs that explicitly activate
|
||||
* them via their name.
|
||||
* @see org.quartz.JobListener#getName
|
||||
* @see org.quartz.JobDetail#addJobListener
|
||||
* @see JobDetailBean#setJobListenerNames
|
||||
*/
|
||||
public void setJobListeners(JobListener[] jobListeners) {
|
||||
this.jobListeners = jobListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify global Quartz TriggerListeners to be registered with the Scheduler.
|
||||
* Such TriggerListeners will apply to all Triggers in the Scheduler.
|
||||
*/
|
||||
public void setGlobalTriggerListeners(TriggerListener[] globalTriggerListeners) {
|
||||
this.globalTriggerListeners = globalTriggerListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify named Quartz TriggerListeners to be registered with the Scheduler.
|
||||
* Such TriggerListeners will only apply to Triggers that explicitly activate
|
||||
* them via their name.
|
||||
* @see org.quartz.TriggerListener#getName
|
||||
* @see org.quartz.Trigger#addTriggerListener
|
||||
* @see CronTriggerBean#setTriggerListenerNames
|
||||
* @see SimpleTriggerBean#setTriggerListenerNames
|
||||
*/
|
||||
public void setTriggerListeners(TriggerListener[] triggerListeners) {
|
||||
this.triggerListeners = triggerListeners;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the transaction manager to be used for registering jobs and triggers
|
||||
* that are defined by this SchedulerFactoryBean. Default is none; setting
|
||||
* this only makes sense when specifying a DataSource for the Scheduler.
|
||||
*/
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register jobs and triggers (within a transaction, if possible).
|
||||
*/
|
||||
protected void registerJobsAndTriggers() throws SchedulerException {
|
||||
TransactionStatus transactionStatus = null;
|
||||
if (this.transactionManager != null) {
|
||||
transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
|
||||
}
|
||||
try {
|
||||
|
||||
if (this.jobSchedulingDataLocations != null) {
|
||||
ClassLoadHelper clh = new ResourceLoaderClassLoadHelper(this.resourceLoader);
|
||||
clh.initialize();
|
||||
try {
|
||||
// Quartz 1.8 or higher?
|
||||
Class dataProcessorClass = getClass().getClassLoader().loadClass("org.quartz.xml.XMLSchedulingDataProcessor");
|
||||
logger.debug("Using Quartz 1.8 XMLSchedulingDataProcessor");
|
||||
Object dataProcessor = dataProcessorClass.getConstructor(ClassLoadHelper.class).newInstance(clh);
|
||||
Method processFileAndScheduleJobs = dataProcessorClass.getMethod("processFileAndScheduleJobs", String.class, Scheduler.class);
|
||||
for (String location : this.jobSchedulingDataLocations) {
|
||||
processFileAndScheduleJobs.invoke(dataProcessor, location, getScheduler());
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// Quartz 1.6
|
||||
Class dataProcessorClass = getClass().getClassLoader().loadClass("org.quartz.xml.JobSchedulingDataProcessor");
|
||||
logger.debug("Using Quartz 1.6 JobSchedulingDataProcessor");
|
||||
Object dataProcessor = dataProcessorClass.getConstructor(ClassLoadHelper.class, boolean.class, boolean.class).newInstance(clh, true, true);
|
||||
Method processFileAndScheduleJobs = dataProcessorClass.getMethod("processFileAndScheduleJobs", String.class, Scheduler.class, boolean.class);
|
||||
for (String location : this.jobSchedulingDataLocations) {
|
||||
processFileAndScheduleJobs.invoke(dataProcessor, location, getScheduler(), this.overwriteExistingJobs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register JobDetails.
|
||||
if (this.jobDetails != null) {
|
||||
for (JobDetail jobDetail : this.jobDetails) {
|
||||
addJobToScheduler(jobDetail);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create empty list for easier checks when registering triggers.
|
||||
this.jobDetails = new LinkedList<JobDetail>();
|
||||
}
|
||||
|
||||
// Register Calendars.
|
||||
if (this.calendars != null) {
|
||||
for (String calendarName : this.calendars.keySet()) {
|
||||
Calendar calendar = this.calendars.get(calendarName);
|
||||
getScheduler().addCalendar(calendarName, calendar, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Register Triggers.
|
||||
if (this.triggers != null) {
|
||||
for (Trigger trigger : this.triggers) {
|
||||
addTriggerToScheduler(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Throwable ex) {
|
||||
if (transactionStatus != null) {
|
||||
try {
|
||||
this.transactionManager.rollback(transactionStatus);
|
||||
}
|
||||
catch (TransactionException tex) {
|
||||
logger.error("Job registration exception overridden by rollback exception", ex);
|
||||
throw tex;
|
||||
}
|
||||
}
|
||||
if (ex instanceof SchedulerException) {
|
||||
throw (SchedulerException) ex;
|
||||
}
|
||||
if (ex instanceof Exception) {
|
||||
throw new SchedulerException("Registration of jobs and triggers failed: " + ex.getMessage(), ex);
|
||||
}
|
||||
throw new SchedulerException("Registration of jobs and triggers failed: " + ex.getMessage());
|
||||
}
|
||||
|
||||
if (transactionStatus != null) {
|
||||
this.transactionManager.commit(transactionStatus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given job to the Scheduler, if it doesn't already exist.
|
||||
* Overwrites the job in any case if "overwriteExistingJobs" is set.
|
||||
* @param jobDetail the job to add
|
||||
* @return <code>true</code> if the job was actually added,
|
||||
* <code>false</code> if it already existed before
|
||||
* @see #setOverwriteExistingJobs
|
||||
*/
|
||||
private boolean addJobToScheduler(JobDetail jobDetail) throws SchedulerException {
|
||||
if (this.overwriteExistingJobs || !jobDetailExists(jobDetail)) {
|
||||
getScheduler().addJob(jobDetail, true);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given trigger to the Scheduler, if it doesn't already exist.
|
||||
* Overwrites the trigger in any case if "overwriteExistingJobs" is set.
|
||||
* @param trigger the trigger to add
|
||||
* @return <code>true</code> if the trigger was actually added,
|
||||
* <code>false</code> if it already existed before
|
||||
* @see #setOverwriteExistingJobs
|
||||
*/
|
||||
private boolean addTriggerToScheduler(Trigger trigger) throws SchedulerException {
|
||||
boolean triggerExists = triggerExists(trigger);
|
||||
if (!triggerExists || this.overwriteExistingJobs) {
|
||||
// Check if the Trigger is aware of an associated JobDetail.
|
||||
JobDetail jobDetail = findJobDetail(trigger);
|
||||
if (jobDetail != null) {
|
||||
// Automatically register the JobDetail too.
|
||||
if (!this.jobDetails.contains(jobDetail) && addJobToScheduler(jobDetail)) {
|
||||
this.jobDetails.add(jobDetail);
|
||||
}
|
||||
}
|
||||
if (!triggerExists) {
|
||||
try {
|
||||
getScheduler().scheduleJob(trigger);
|
||||
}
|
||||
catch (ObjectAlreadyExistsException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Unexpectedly found existing trigger, assumably due to cluster race condition: " +
|
||||
ex.getMessage() + " - can safely be ignored");
|
||||
}
|
||||
if (this.overwriteExistingJobs) {
|
||||
rescheduleJob(trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
rescheduleJob(trigger);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private JobDetail findJobDetail(Trigger trigger) {
|
||||
if (trigger instanceof JobDetailAwareTrigger) {
|
||||
return ((JobDetailAwareTrigger) trigger).getJobDetail();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Map jobDataMap = (Map) ReflectionUtils.invokeMethod(Trigger.class.getMethod("getJobDataMap"), trigger);
|
||||
return (JobDetail) jobDataMap.get(JobDetailAwareTrigger.JOB_DETAIL_KEY);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Inconsistent Quartz API: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
|
||||
private boolean jobDetailExists(JobDetail jobDetail) throws SchedulerException {
|
||||
if (jobKeyClass != null) {
|
||||
try {
|
||||
Method getJobDetail = Scheduler.class.getMethod("getJobDetail", jobKeyClass);
|
||||
Object key = ReflectionUtils.invokeMethod(JobDetail.class.getMethod("getKey"), jobDetail);
|
||||
return (ReflectionUtils.invokeMethod(getJobDetail, getScheduler(), key) != null);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (getScheduler().getJobDetail(jobDetail.getName(), jobDetail.getGroup()) != null);
|
||||
}
|
||||
}
|
||||
|
||||
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
|
||||
private boolean triggerExists(Trigger trigger) throws SchedulerException {
|
||||
if (triggerKeyClass != null) {
|
||||
try {
|
||||
Method getTrigger = Scheduler.class.getMethod("getTrigger", triggerKeyClass);
|
||||
Object key = ReflectionUtils.invokeMethod(Trigger.class.getMethod("getKey"), trigger);
|
||||
return (ReflectionUtils.invokeMethod(getTrigger, getScheduler(), key) != null);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (getScheduler().getTrigger(trigger.getName(), trigger.getGroup()) != null);
|
||||
}
|
||||
}
|
||||
|
||||
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
|
||||
private void rescheduleJob(Trigger trigger) throws SchedulerException {
|
||||
if (triggerKeyClass != null) {
|
||||
try {
|
||||
Method rescheduleJob = Scheduler.class.getMethod("rescheduleJob", triggerKeyClass, Trigger.class);
|
||||
Object key = ReflectionUtils.invokeMethod(Trigger.class.getMethod("getKey"), trigger);
|
||||
ReflectionUtils.invokeMethod(rescheduleJob, getScheduler(), key, trigger);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Inconsistent Quartz 2.0 API: " + ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
getScheduler().rescheduleJob(trigger.getName(), trigger.getGroup(), trigger);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register all specified listeners with the Scheduler.
|
||||
*/
|
||||
protected void registerListeners() throws SchedulerException {
|
||||
Object target;
|
||||
boolean quartz2;
|
||||
try {
|
||||
Method getListenerManager = Scheduler.class.getMethod("getListenerManager");
|
||||
target = ReflectionUtils.invokeMethod(getListenerManager, getScheduler());
|
||||
quartz2 = true;
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
target = getScheduler();
|
||||
quartz2 = false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.schedulerListeners != null) {
|
||||
Method addSchedulerListener = target.getClass().getMethod("addSchedulerListener", SchedulerListener.class);
|
||||
for (SchedulerListener listener : this.schedulerListeners) {
|
||||
ReflectionUtils.invokeMethod(addSchedulerListener, target, listener);
|
||||
}
|
||||
}
|
||||
if (this.globalJobListeners != null) {
|
||||
Method addJobListener = target.getClass().getMethod(
|
||||
(quartz2 ? "addJobListener" : "addGlobalJobListener"), JobListener.class);
|
||||
for (JobListener listener : this.globalJobListeners) {
|
||||
ReflectionUtils.invokeMethod(addJobListener, target, listener);
|
||||
}
|
||||
}
|
||||
if (this.jobListeners != null) {
|
||||
for (JobListener listener : this.jobListeners) {
|
||||
if (quartz2) {
|
||||
throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " +
|
||||
"manually register a Matcher against the Quartz ListenerManager instead");
|
||||
}
|
||||
getScheduler().addJobListener(listener);
|
||||
}
|
||||
}
|
||||
if (this.globalTriggerListeners != null) {
|
||||
Method addTriggerListener = target.getClass().getMethod(
|
||||
(quartz2 ? "addTriggerListener" : "addGlobalTriggerListener"), TriggerListener.class);
|
||||
for (TriggerListener listener : this.globalTriggerListeners) {
|
||||
ReflectionUtils.invokeMethod(addTriggerListener, target, listener);
|
||||
}
|
||||
}
|
||||
if (this.triggerListeners != null) {
|
||||
for (TriggerListener listener : this.triggerListeners) {
|
||||
if (quartz2) {
|
||||
throw new IllegalStateException("Non-global TriggerListeners not supported on Quartz 2 - " +
|
||||
"manually register a Matcher against the Quartz ListenerManager instead");
|
||||
}
|
||||
getScheduler().addTriggerListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Expected Quartz API not present: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Template method that determines the Scheduler to operate on.
|
||||
* To be implemented by subclasses.
|
||||
*/
|
||||
protected abstract Scheduler getScheduler();
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.impl.SchedulerRepository;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
|
||||
/**
|
||||
* Spring bean-style class for accessing a Quartz Scheduler, i.e. for registering jobs,
|
||||
* triggers and listeners on a given {@link org.quartz.Scheduler} instance.
|
||||
*
|
||||
* <p>Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5.6
|
||||
* @see #setScheduler
|
||||
* @see #setSchedulerName
|
||||
*/
|
||||
public class SchedulerAccessorBean extends SchedulerAccessor implements BeanFactoryAware, InitializingBean {
|
||||
|
||||
private String schedulerName;
|
||||
|
||||
private Scheduler scheduler;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the Quartz Scheduler to operate on via its scheduler name in the Spring
|
||||
* application context or also in the Quartz {@link org.quartz.impl.SchedulerRepository}.
|
||||
* <p>Schedulers can be registered in the repository through custom bootstrapping,
|
||||
* e.g. via the {@link org.quartz.impl.StdSchedulerFactory} or
|
||||
* {@link org.quartz.impl.DirectSchedulerFactory} factory classes.
|
||||
* However, in general, it's preferable to use Spring's {@link SchedulerFactoryBean}
|
||||
* which includes the job/trigger/listener capabilities of this accessor as well.
|
||||
*/
|
||||
public void setSchedulerName(String schedulerName) {
|
||||
this.schedulerName = schedulerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the Quartz Scheduler instance to operate on.
|
||||
*/
|
||||
public void setScheduler(Scheduler scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Quartz Scheduler instance that this accessor operates on.
|
||||
*/
|
||||
@Override
|
||||
public Scheduler getScheduler() {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws SchedulerException {
|
||||
if (this.scheduler == null) {
|
||||
if (this.schedulerName != null) {
|
||||
this.scheduler = findScheduler(this.schedulerName);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No Scheduler specified");
|
||||
}
|
||||
}
|
||||
registerListeners();
|
||||
registerJobsAndTriggers();
|
||||
}
|
||||
|
||||
protected Scheduler findScheduler(String schedulerName) throws SchedulerException {
|
||||
if (this.beanFactory instanceof ListableBeanFactory) {
|
||||
ListableBeanFactory lbf = (ListableBeanFactory) this.beanFactory;
|
||||
String[] beanNames = lbf.getBeanNamesForType(Scheduler.class);
|
||||
for (int i = 0; i < beanNames.length; i++) {
|
||||
Scheduler schedulerBean = (Scheduler) lbf.getBean(beanNames[i]);
|
||||
if (schedulerName.equals(schedulerBean.getSchedulerName())) {
|
||||
return schedulerBean;
|
||||
}
|
||||
}
|
||||
}
|
||||
Scheduler schedulerInRepo = SchedulerRepository.getInstance().lookup(schedulerName);
|
||||
if (schedulerInRepo == null) {
|
||||
throw new IllegalStateException("No Scheduler named '" + schedulerName + "' found");
|
||||
}
|
||||
return schedulerInRepo;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import org.quartz.SchedulerContext;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
|
||||
/**
|
||||
* Callback interface to be implemented by Spring-managed
|
||||
* Quartz artifacts that need access to the SchedulerContext
|
||||
* (without having natural access to it).
|
||||
*
|
||||
* <p>Currently only supported for custom JobFactory implementations
|
||||
* that are passed in via Spring's SchedulerFactoryBean.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 2.0
|
||||
* @see org.quartz.spi.JobFactory
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
*/
|
||||
public interface SchedulerContextAware extends Aware {
|
||||
|
||||
/**
|
||||
* Set the SchedulerContext of the current Quartz Scheduler.
|
||||
* @see org.quartz.Scheduler#getContext()
|
||||
*/
|
||||
void setSchedulerContext(SchedulerContext schedulerContext);
|
||||
|
||||
}
|
||||
@@ -1,762 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executor;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.SchedulerFactory;
|
||||
import org.quartz.impl.RemoteScheduler;
|
||||
import org.quartz.impl.SchedulerRepository;
|
||||
import org.quartz.impl.StdSchedulerFactory;
|
||||
import org.quartz.simpl.SimpleThreadPool;
|
||||
import org.quartz.spi.JobFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.scheduling.SchedulingException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} that creates and configures a Quartz {@link org.quartz.Scheduler},
|
||||
* manages its lifecycle as part of the Spring application context, and exposes the
|
||||
* Scheduler as bean reference for dependency injection.
|
||||
*
|
||||
* <p>Allows registration of JobDetails, Calendars and Triggers, automatically
|
||||
* starting the scheduler on initialization and shutting it down on destruction.
|
||||
* In scenarios that just require static registration of jobs at startup, there
|
||||
* is no need to access the Scheduler instance itself in application code.
|
||||
*
|
||||
* <p>For dynamic registration of jobs at runtime, use a bean reference to
|
||||
* this SchedulerFactoryBean to get direct access to the Quartz Scheduler
|
||||
* (<code>org.quartz.Scheduler</code>). This allows you to create new jobs
|
||||
* and triggers, and also to control and monitor the entire Scheduler.
|
||||
*
|
||||
* <p>Note that Quartz instantiates a new Job for each execution, in
|
||||
* contrast to Timer which uses a TimerTask instance that is shared
|
||||
* between repeated executions. Just JobDetail descriptors are shared.
|
||||
*
|
||||
* <p>When using persistent jobs, it is strongly recommended to perform all
|
||||
* operations on the Scheduler within Spring-managed (or plain JTA) transactions.
|
||||
* Else, database locking will not properly work and might even break.
|
||||
* (See {@link #setDataSource setDataSource} javadoc for details.)
|
||||
*
|
||||
* <p>The preferred way to achieve transactional execution is to demarcate
|
||||
* declarative transactions at the business facade level, which will
|
||||
* automatically apply to Scheduler operations performed within those scopes.
|
||||
* Alternatively, you may add transactional advice for the Scheduler itself.
|
||||
*
|
||||
* <p>Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setDataSource
|
||||
* @see org.quartz.Scheduler
|
||||
* @see org.quartz.SchedulerFactory
|
||||
* @see org.quartz.impl.StdSchedulerFactory
|
||||
* @see org.springframework.transaction.interceptor.TransactionProxyFactoryBean
|
||||
*/
|
||||
public class SchedulerFactoryBean extends SchedulerAccessor implements FactoryBean<Scheduler>, BeanNameAware,
|
||||
ApplicationContextAware, InitializingBean, DisposableBean, SmartLifecycle {
|
||||
|
||||
public static final String PROP_THREAD_COUNT = "org.quartz.threadPool.threadCount";
|
||||
|
||||
public static final int DEFAULT_THREAD_COUNT = 10;
|
||||
|
||||
|
||||
private static final ThreadLocal<ResourceLoader> configTimeResourceLoaderHolder =
|
||||
new ThreadLocal<ResourceLoader>();
|
||||
|
||||
private static final ThreadLocal<Executor> configTimeTaskExecutorHolder =
|
||||
new ThreadLocal<Executor>();
|
||||
|
||||
private static final ThreadLocal<DataSource> configTimeDataSourceHolder =
|
||||
new ThreadLocal<DataSource>();
|
||||
|
||||
private static final ThreadLocal<DataSource> configTimeNonTransactionalDataSourceHolder =
|
||||
new ThreadLocal<DataSource>();
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader for the currently configured Quartz Scheduler,
|
||||
* to be used by ResourceLoaderClassLoadHelper.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setApplicationContext
|
||||
* @see ResourceLoaderClassLoadHelper
|
||||
*/
|
||||
public static ResourceLoader getConfigTimeResourceLoader() {
|
||||
return configTimeResourceLoaderHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TaskExecutor for the currently configured Quartz Scheduler,
|
||||
* to be used by LocalTaskExecutorThreadPool.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setTaskExecutor
|
||||
* @see LocalTaskExecutorThreadPool
|
||||
*/
|
||||
public static Executor getConfigTimeTaskExecutor() {
|
||||
return configTimeTaskExecutorHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DataSource for the currently configured Quartz Scheduler,
|
||||
* to be used by LocalDataSourceJobStore.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setDataSource
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public static DataSource getConfigTimeDataSource() {
|
||||
return configTimeDataSourceHolder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the non-transactional DataSource for the currently configured
|
||||
* Quartz Scheduler, to be used by LocalDataSourceJobStore.
|
||||
* <p>This instance will be set before initialization of the corresponding
|
||||
* Scheduler, and reset immediately afterwards. It is thus only available
|
||||
* during configuration.
|
||||
* @see #setNonTransactionalDataSource
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public static DataSource getConfigTimeNonTransactionalDataSource() {
|
||||
return configTimeNonTransactionalDataSourceHolder.get();
|
||||
}
|
||||
|
||||
|
||||
private Class<?> schedulerFactoryClass = StdSchedulerFactory.class;
|
||||
|
||||
private String schedulerName;
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private Properties quartzProperties;
|
||||
|
||||
|
||||
private Executor taskExecutor;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private DataSource nonTransactionalDataSource;
|
||||
|
||||
|
||||
private Map schedulerContextMap;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String applicationContextSchedulerContextKey;
|
||||
|
||||
private JobFactory jobFactory;
|
||||
|
||||
private boolean jobFactorySet = false;
|
||||
|
||||
|
||||
private boolean autoStartup = true;
|
||||
|
||||
private int startupDelay = 0;
|
||||
|
||||
private int phase = Integer.MAX_VALUE;
|
||||
|
||||
private boolean exposeSchedulerInRepository = false;
|
||||
|
||||
private boolean waitForJobsToCompleteOnShutdown = false;
|
||||
|
||||
|
||||
private Scheduler scheduler;
|
||||
|
||||
|
||||
/**
|
||||
* Set the Quartz SchedulerFactory implementation to use.
|
||||
* <p>Default is StdSchedulerFactory, reading in the standard
|
||||
* <code>quartz.properties</code> from <code>quartz.jar</code>.
|
||||
* To use custom Quartz properties, specify the "configLocation"
|
||||
* or "quartzProperties" bean property on this FactoryBean.
|
||||
* @see org.quartz.impl.StdSchedulerFactory
|
||||
* @see #setConfigLocation
|
||||
* @see #setQuartzProperties
|
||||
*/
|
||||
public void setSchedulerFactoryClass(Class schedulerFactoryClass) {
|
||||
if (schedulerFactoryClass == null || !SchedulerFactory.class.isAssignableFrom(schedulerFactoryClass)) {
|
||||
throw new IllegalArgumentException("schedulerFactoryClass must implement [org.quartz.SchedulerFactory]");
|
||||
}
|
||||
this.schedulerFactoryClass = schedulerFactoryClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the Scheduler to create via the SchedulerFactory.
|
||||
* <p>If not specified, the bean name will be used as default scheduler name.
|
||||
* @see #setBeanName
|
||||
* @see org.quartz.SchedulerFactory#getScheduler()
|
||||
* @see org.quartz.SchedulerFactory#getScheduler(String)
|
||||
*/
|
||||
public void setSchedulerName(String schedulerName) {
|
||||
this.schedulerName = schedulerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the location of the Quartz properties config file, for example
|
||||
* as classpath resource "classpath:quartz.properties".
|
||||
* <p>Note: Can be omitted when all necessary properties are specified
|
||||
* locally via this bean, or when relying on Quartz' default configuration.
|
||||
* @see #setQuartzProperties
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocation = configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Quartz properties, like "org.quartz.threadPool.class".
|
||||
* <p>Can be used to override values in a Quartz properties config file,
|
||||
* or to specify all necessary properties locally.
|
||||
* @see #setConfigLocation
|
||||
*/
|
||||
public void setQuartzProperties(Properties quartzProperties) {
|
||||
this.quartzProperties = quartzProperties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the Spring TaskExecutor to use as Quartz backend.
|
||||
* Exposed as thread pool through the Quartz SPI.
|
||||
* <p>Can be used to assign a JDK 1.5 ThreadPoolExecutor or a CommonJ
|
||||
* WorkManager as Quartz backend, to avoid Quartz's manual thread creation.
|
||||
* <p>By default, a Quartz SimpleThreadPool will be used, configured through
|
||||
* the corresponding Quartz properties.
|
||||
* @see #setQuartzProperties
|
||||
* @see LocalTaskExecutorThreadPool
|
||||
* @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
|
||||
* @see org.springframework.scheduling.commonj.WorkManagerTaskExecutor
|
||||
*/
|
||||
public void setTaskExecutor(Executor taskExecutor) {
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default DataSource to be used by the Scheduler. If set,
|
||||
* this will override corresponding settings in Quartz properties.
|
||||
* <p>Note: If this is set, the Quartz settings should not define
|
||||
* a job store "dataSource" to avoid meaningless double configuration.
|
||||
* <p>A Spring-specific subclass of Quartz' JobStoreCMT will be used.
|
||||
* It is therefore strongly recommended to perform all operations on
|
||||
* the Scheduler within Spring-managed (or plain JTA) transactions.
|
||||
* Else, database locking will not properly work and might even break
|
||||
* (e.g. if trying to obtain a lock on Oracle without a transaction).
|
||||
* <p>Supports both transactional and non-transactional DataSource access.
|
||||
* With a non-XA DataSource and local Spring transactions, a single DataSource
|
||||
* argument is sufficient. In case of an XA DataSource and global JTA transactions,
|
||||
* SchedulerFactoryBean's "nonTransactionalDataSource" property should be set,
|
||||
* passing in a non-XA DataSource that will not participate in global transactions.
|
||||
* @see #setNonTransactionalDataSource
|
||||
* @see #setQuartzProperties
|
||||
* @see #setTransactionManager
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public void setDataSource(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DataSource to be used by the Scheduler <i>for non-transactional access</i>.
|
||||
* <p>This is only necessary if the default DataSource is an XA DataSource that will
|
||||
* always participate in transactions: A non-XA version of that DataSource should
|
||||
* be specified as "nonTransactionalDataSource" in such a scenario.
|
||||
* <p>This is not relevant with a local DataSource instance and Spring transactions.
|
||||
* Specifying a single default DataSource as "dataSource" is sufficient there.
|
||||
* @see #setDataSource
|
||||
* @see LocalDataSourceJobStore
|
||||
*/
|
||||
public void setNonTransactionalDataSource(DataSource nonTransactionalDataSource) {
|
||||
this.nonTransactionalDataSource = nonTransactionalDataSource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register objects in the Scheduler context via a given Map.
|
||||
* These objects will be available to any Job that runs in this Scheduler.
|
||||
* <p>Note: When using persistent Jobs whose JobDetail will be kept in the
|
||||
* database, do not put Spring-managed beans or an ApplicationContext
|
||||
* reference into the JobDataMap but rather into the SchedulerContext.
|
||||
* @param schedulerContextAsMap Map with String keys and any objects as
|
||||
* values (for example Spring-managed beans)
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setSchedulerContextAsMap(Map schedulerContextAsMap) {
|
||||
this.schedulerContextMap = schedulerContextAsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key of an ApplicationContext reference to expose in the
|
||||
* SchedulerContext, for example "applicationContext". Default is none.
|
||||
* Only applicable when running in a Spring ApplicationContext.
|
||||
* <p>Note: When using persistent Jobs whose JobDetail will be kept in the
|
||||
* database, do not put an ApplicationContext reference into the JobDataMap
|
||||
* but rather into the SchedulerContext.
|
||||
* <p>In case of a QuartzJobBean, the reference will be applied to the Job
|
||||
* instance as bean property. An "applicationContext" attribute will
|
||||
* correspond to a "setApplicationContext" method in that scenario.
|
||||
* <p>Note that BeanFactory callback interfaces like ApplicationContextAware
|
||||
* are not automatically applied to Quartz Job instances, because Quartz
|
||||
* itself is reponsible for the lifecycle of its Jobs.
|
||||
* @see JobDetailBean#setApplicationContextJobDataKey
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
public void setApplicationContextSchedulerContextKey(String applicationContextSchedulerContextKey) {
|
||||
this.applicationContextSchedulerContextKey = applicationContextSchedulerContextKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Quartz JobFactory to use for this Scheduler.
|
||||
* <p>Default is Spring's {@link AdaptableJobFactory}, which supports
|
||||
* {@link java.lang.Runnable} objects as well as standard Quartz
|
||||
* {@link org.quartz.Job} instances. Note that this default only applies
|
||||
* to a <i>local</i> Scheduler, not to a RemoteScheduler (where setting
|
||||
* a custom JobFactory is not supported by Quartz).
|
||||
* <p>Specify an instance of Spring's {@link SpringBeanJobFactory} here
|
||||
* (typically as an inner bean definition) to automatically populate a job's
|
||||
* bean properties from the specified job data map and scheduler context.
|
||||
* @see AdaptableJobFactory
|
||||
* @see SpringBeanJobFactory
|
||||
*/
|
||||
public void setJobFactory(JobFactory jobFactory) {
|
||||
this.jobFactory = jobFactory;
|
||||
this.jobFactorySet = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to automatically start the scheduler after initialization.
|
||||
* <p>Default is "true"; set this to "false" to allow for manual startup.
|
||||
*/
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this scheduler is configured for auto-startup. If "true",
|
||||
* the scheduler will start after the context is refreshed and after the
|
||||
* start delay, if any.
|
||||
*/
|
||||
public boolean isAutoStartup() {
|
||||
return this.autoStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the phase in which this scheduler should be started and
|
||||
* stopped. The startup order proceeds from lowest to highest, and
|
||||
* the shutdown order is the reverse of that. By default this value
|
||||
* is Integer.MAX_VALUE meaning that this scheduler starts as late
|
||||
* as possible and stops as soon as possible.
|
||||
*/
|
||||
public void setPhase(int phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the phase in which this scheduler will be started and stopped.
|
||||
*/
|
||||
public int getPhase() {
|
||||
return this.phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of seconds to wait after initialization before
|
||||
* starting the scheduler asynchronously. Default is 0, meaning
|
||||
* immediate synchronous startup on initialization of this bean.
|
||||
* <p>Setting this to 10 or 20 seconds makes sense if no jobs
|
||||
* should be run before the entire application has started up.
|
||||
*/
|
||||
public void setStartupDelay(int startupDelay) {
|
||||
this.startupDelay = startupDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to expose the Spring-managed {@link Scheduler} instance in the
|
||||
* Quartz {@link SchedulerRepository}. Default is "false", since the Spring-managed
|
||||
* Scheduler is usually exclusively intended for access within the Spring context.
|
||||
* <p>Switch this flag to "true" in order to expose the Scheduler globally.
|
||||
* This is not recommended unless you have an existing Spring application that
|
||||
* relies on this behavior. Note that such global exposure was the accidental
|
||||
* default in earlier Spring versions; this has been fixed as of Spring 2.5.6.
|
||||
*/
|
||||
public void setExposeSchedulerInRepository(boolean exposeSchedulerInRepository) {
|
||||
this.exposeSchedulerInRepository = exposeSchedulerInRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to wait for running jobs to complete on shutdown.
|
||||
* <p>Default is "false". Switch this to "true" if you prefer
|
||||
* fully completed jobs at the expense of a longer shutdown phase.
|
||||
* @see org.quartz.Scheduler#shutdown(boolean)
|
||||
*/
|
||||
public void setWaitForJobsToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
|
||||
this.waitForJobsToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
|
||||
}
|
||||
|
||||
|
||||
public void setBeanName(String name) {
|
||||
if (this.schedulerName == null) {
|
||||
this.schedulerName = name;
|
||||
}
|
||||
}
|
||||
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of InitializingBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (this.dataSource == null && this.nonTransactionalDataSource != null) {
|
||||
this.dataSource = this.nonTransactionalDataSource;
|
||||
}
|
||||
|
||||
if (this.applicationContext != null && this.resourceLoader == null) {
|
||||
this.resourceLoader = this.applicationContext;
|
||||
}
|
||||
|
||||
// Create SchedulerFactory instance.
|
||||
SchedulerFactory schedulerFactory = (SchedulerFactory)
|
||||
BeanUtils.instantiateClass(this.schedulerFactoryClass);
|
||||
|
||||
initSchedulerFactory(schedulerFactory);
|
||||
|
||||
if (this.resourceLoader != null) {
|
||||
// Make given ResourceLoader available for SchedulerFactory configuration.
|
||||
configTimeResourceLoaderHolder.set(this.resourceLoader);
|
||||
}
|
||||
if (this.taskExecutor != null) {
|
||||
// Make given TaskExecutor available for SchedulerFactory configuration.
|
||||
configTimeTaskExecutorHolder.set(this.taskExecutor);
|
||||
}
|
||||
if (this.dataSource != null) {
|
||||
// Make given DataSource available for SchedulerFactory configuration.
|
||||
configTimeDataSourceHolder.set(this.dataSource);
|
||||
}
|
||||
if (this.nonTransactionalDataSource != null) {
|
||||
// Make given non-transactional DataSource available for SchedulerFactory configuration.
|
||||
configTimeNonTransactionalDataSourceHolder.set(this.nonTransactionalDataSource);
|
||||
}
|
||||
|
||||
|
||||
// Get Scheduler instance from SchedulerFactory.
|
||||
try {
|
||||
this.scheduler = createScheduler(schedulerFactory, this.schedulerName);
|
||||
populateSchedulerContext();
|
||||
|
||||
if (!this.jobFactorySet && !(this.scheduler instanceof RemoteScheduler)) {
|
||||
// Use AdaptableJobFactory as default for a local Scheduler, unless when
|
||||
// explicitly given a null value through the "jobFactory" bean property.
|
||||
this.jobFactory = new AdaptableJobFactory();
|
||||
}
|
||||
if (this.jobFactory != null) {
|
||||
if (this.jobFactory instanceof SchedulerContextAware) {
|
||||
((SchedulerContextAware) this.jobFactory).setSchedulerContext(this.scheduler.getContext());
|
||||
}
|
||||
this.scheduler.setJobFactory(this.jobFactory);
|
||||
}
|
||||
}
|
||||
|
||||
finally {
|
||||
if (this.resourceLoader != null) {
|
||||
configTimeResourceLoaderHolder.remove();
|
||||
}
|
||||
if (this.taskExecutor != null) {
|
||||
configTimeTaskExecutorHolder.remove();
|
||||
}
|
||||
if (this.dataSource != null) {
|
||||
configTimeDataSourceHolder.remove();
|
||||
}
|
||||
if (this.nonTransactionalDataSource != null) {
|
||||
configTimeNonTransactionalDataSourceHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
registerListeners();
|
||||
registerJobsAndTriggers();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load and/or apply Quartz properties to the given SchedulerFactory.
|
||||
* @param schedulerFactory the SchedulerFactory to initialize
|
||||
*/
|
||||
private void initSchedulerFactory(SchedulerFactory schedulerFactory)
|
||||
throws SchedulerException, IOException {
|
||||
|
||||
if (!(schedulerFactory instanceof StdSchedulerFactory)) {
|
||||
if (this.configLocation != null || this.quartzProperties != null ||
|
||||
this.taskExecutor != null || this.dataSource != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"StdSchedulerFactory required for applying Quartz properties: " + schedulerFactory);
|
||||
}
|
||||
// Otherwise assume that no initialization is necessary...
|
||||
return;
|
||||
}
|
||||
|
||||
Properties mergedProps = new Properties();
|
||||
|
||||
if (this.resourceLoader != null) {
|
||||
mergedProps.setProperty(StdSchedulerFactory.PROP_SCHED_CLASS_LOAD_HELPER_CLASS,
|
||||
ResourceLoaderClassLoadHelper.class.getName());
|
||||
}
|
||||
|
||||
if (this.taskExecutor != null) {
|
||||
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS,
|
||||
LocalTaskExecutorThreadPool.class.getName());
|
||||
}
|
||||
else {
|
||||
// Set necessary default properties here, as Quartz will not apply
|
||||
// its default configuration when explicitly given properties.
|
||||
mergedProps.setProperty(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName());
|
||||
mergedProps.setProperty(PROP_THREAD_COUNT, Integer.toString(DEFAULT_THREAD_COUNT));
|
||||
}
|
||||
|
||||
if (this.configLocation != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading Quartz config from [" + this.configLocation + "]");
|
||||
}
|
||||
PropertiesLoaderUtils.fillProperties(mergedProps, this.configLocation);
|
||||
}
|
||||
|
||||
CollectionUtils.mergePropertiesIntoMap(this.quartzProperties, mergedProps);
|
||||
|
||||
if (this.dataSource != null) {
|
||||
mergedProps.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, LocalDataSourceJobStore.class.getName());
|
||||
}
|
||||
|
||||
// Make sure to set the scheduler name as configured in the Spring configuration.
|
||||
if (this.schedulerName != null) {
|
||||
mergedProps.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, this.schedulerName);
|
||||
}
|
||||
|
||||
((StdSchedulerFactory) schedulerFactory).initialize(mergedProps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Scheduler instance for the given factory and scheduler name.
|
||||
* Called by {@link #afterPropertiesSet}.
|
||||
* <p>The default implementation invokes SchedulerFactory's <code>getScheduler</code>
|
||||
* method. Can be overridden for custom Scheduler creation.
|
||||
* @param schedulerFactory the factory to create the Scheduler with
|
||||
* @param schedulerName the name of the scheduler to create
|
||||
* @return the Scheduler instance
|
||||
* @throws SchedulerException if thrown by Quartz methods
|
||||
* @see #afterPropertiesSet
|
||||
* @see org.quartz.SchedulerFactory#getScheduler
|
||||
*/
|
||||
protected Scheduler createScheduler(SchedulerFactory schedulerFactory, String schedulerName)
|
||||
throws SchedulerException {
|
||||
|
||||
// Override thread context ClassLoader to work around naive Quartz ClassLoadHelper loading.
|
||||
Thread currentThread = Thread.currentThread();
|
||||
ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
|
||||
boolean overrideClassLoader = (this.resourceLoader != null &&
|
||||
!this.resourceLoader.getClassLoader().equals(threadContextClassLoader));
|
||||
if (overrideClassLoader) {
|
||||
currentThread.setContextClassLoader(this.resourceLoader.getClassLoader());
|
||||
}
|
||||
try {
|
||||
SchedulerRepository repository = SchedulerRepository.getInstance();
|
||||
synchronized (repository) {
|
||||
Scheduler existingScheduler = (schedulerName != null ? repository.lookup(schedulerName) : null);
|
||||
Scheduler newScheduler = schedulerFactory.getScheduler();
|
||||
if (newScheduler == existingScheduler) {
|
||||
throw new IllegalStateException("Active Scheduler of name '" + schedulerName + "' already registered " +
|
||||
"in Quartz SchedulerRepository. Cannot create a new Spring-managed Scheduler of the same name!");
|
||||
}
|
||||
if (!this.exposeSchedulerInRepository) {
|
||||
// Need to remove it in this case, since Quartz shares the Scheduler instance by default!
|
||||
SchedulerRepository.getInstance().remove(newScheduler.getSchedulerName());
|
||||
}
|
||||
return newScheduler;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (overrideClassLoader) {
|
||||
// Reset original thread context ClassLoader.
|
||||
currentThread.setContextClassLoader(threadContextClassLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the specified context attributes and/or the current
|
||||
* ApplicationContext in the Quartz SchedulerContext.
|
||||
*/
|
||||
private void populateSchedulerContext() throws SchedulerException {
|
||||
// Put specified objects into Scheduler context.
|
||||
if (this.schedulerContextMap != null) {
|
||||
this.scheduler.getContext().putAll(this.schedulerContextMap);
|
||||
}
|
||||
|
||||
// Register ApplicationContext in Scheduler context.
|
||||
if (this.applicationContextSchedulerContextKey != null) {
|
||||
if (this.applicationContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"SchedulerFactoryBean needs to be set up in an ApplicationContext " +
|
||||
"to be able to handle an 'applicationContextSchedulerContextKey'");
|
||||
}
|
||||
this.scheduler.getContext().put(this.applicationContextSchedulerContextKey, this.applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start the Quartz Scheduler, respecting the "startupDelay" setting.
|
||||
* @param scheduler the Scheduler to start
|
||||
* @param startupDelay the number of seconds to wait before starting
|
||||
* the Scheduler asynchronously
|
||||
*/
|
||||
protected void startScheduler(final Scheduler scheduler, final int startupDelay) throws SchedulerException {
|
||||
if (startupDelay <= 0) {
|
||||
logger.info("Starting Quartz Scheduler now");
|
||||
scheduler.start();
|
||||
}
|
||||
else {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Will start Quartz Scheduler [" + scheduler.getSchedulerName() +
|
||||
"] in " + startupDelay + " seconds");
|
||||
}
|
||||
Thread schedulerThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(startupDelay * 1000);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
// simply proceed
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Starting Quartz Scheduler now, after delay of " + startupDelay + " seconds");
|
||||
}
|
||||
try {
|
||||
scheduler.start();
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new SchedulingException("Could not start Quartz Scheduler after delay", ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
schedulerThread.setName("Quartz Scheduler [" + scheduler.getSchedulerName() + "]");
|
||||
schedulerThread.setDaemon(true);
|
||||
schedulerThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of FactoryBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public Scheduler getScheduler() {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
public Scheduler getObject() {
|
||||
return this.scheduler;
|
||||
}
|
||||
|
||||
public Class<? extends Scheduler> getObjectType() {
|
||||
return (this.scheduler != null) ? this.scheduler.getClass() : Scheduler.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of Lifecycle interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
public void start() throws SchedulingException {
|
||||
if (this.scheduler != null) {
|
||||
try {
|
||||
startScheduler(this.scheduler, this.startupDelay);
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new SchedulingException("Could not start Quartz Scheduler", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() throws SchedulingException {
|
||||
if (this.scheduler != null) {
|
||||
try {
|
||||
this.scheduler.standby();
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
throw new SchedulingException("Could not stop Quartz Scheduler", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) throws SchedulingException {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
public boolean isRunning() throws SchedulingException {
|
||||
if (this.scheduler != null) {
|
||||
try {
|
||||
return !this.scheduler.isInStandbyMode();
|
||||
}
|
||||
catch (SchedulerException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of DisposableBean interface
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Shut down the Quartz scheduler on bean factory shutdown,
|
||||
* stopping all scheduled jobs.
|
||||
*/
|
||||
public void destroy() throws SchedulerException {
|
||||
logger.info("Shutting down Quartz Scheduler");
|
||||
this.scheduler.shutdown(this.waitForJobsToCompleteOnShutdown);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.quartz.SchedulerConfigException;
|
||||
import org.quartz.simpl.SimpleThreadPool;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.scheduling.SchedulingException;
|
||||
import org.springframework.scheduling.SchedulingTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Subclass of Quartz's SimpleThreadPool that implements Spring's
|
||||
* {@link org.springframework.core.task.TaskExecutor} interface
|
||||
* and listens to Spring lifecycle callbacks.
|
||||
*
|
||||
* <p>Can be shared between a Quartz Scheduler (specified as "taskExecutor")
|
||||
* and other TaskExecutor users, or even used completely independent of
|
||||
* a Quartz Scheduler (as plain TaskExecutor backend).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.quartz.simpl.SimpleThreadPool
|
||||
* @see org.springframework.core.task.TaskExecutor
|
||||
* @see SchedulerFactoryBean#setTaskExecutor
|
||||
*/
|
||||
public class SimpleThreadPoolTaskExecutor extends SimpleThreadPool
|
||||
implements SchedulingTaskExecutor, InitializingBean, DisposableBean {
|
||||
|
||||
private boolean waitForJobsToCompleteOnShutdown = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to wait for running jobs to complete on shutdown.
|
||||
* Default is "false".
|
||||
* @see org.quartz.simpl.SimpleThreadPool#shutdown(boolean)
|
||||
*/
|
||||
public void setWaitForJobsToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
|
||||
this.waitForJobsToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws SchedulerConfigException {
|
||||
initialize();
|
||||
}
|
||||
|
||||
|
||||
public void execute(Runnable task) {
|
||||
Assert.notNull(task, "Runnable must not be null");
|
||||
if (!runInThread(task)) {
|
||||
throw new SchedulingException("Quartz SimpleThreadPool already shut down");
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(Runnable task, long startTimeout) {
|
||||
execute(task);
|
||||
}
|
||||
|
||||
public Future<?> submit(Runnable task) {
|
||||
FutureTask<Object> future = new FutureTask<Object>(task, null);
|
||||
execute(future);
|
||||
return future;
|
||||
}
|
||||
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
FutureTask<T> future = new FutureTask<T>(task);
|
||||
execute(future);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* This task executor prefers short-lived work units.
|
||||
*/
|
||||
public boolean prefersShortLivedTasks() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void destroy() {
|
||||
shutdown(this.waitForJobsToCompleteOnShutdown);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SimpleTrigger;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
|
||||
/**
|
||||
* Convenience subclass of Quartz's {@link org.quartz.SimpleTrigger} class,
|
||||
* making bean-style usage easier.
|
||||
*
|
||||
* <p><code>SimpleTrigger</code> itself is already a JavaBean but lacks sensible defaults.
|
||||
* This class uses the Spring bean name as job name, the Quartz default group
|
||||
* ("DEFAULT") as job group, the current time as start time, and indefinite
|
||||
* repetition, if not specified.
|
||||
*
|
||||
* <p>This class will also register the trigger with the job name and group of
|
||||
* a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
|
||||
* to automatically register a trigger for the corresponding JobDetail,
|
||||
* instead of registering the JobDetail separately.
|
||||
*
|
||||
* <p><b>NOTE: This convenience subclass does not work against Quartz 2.0.</b>
|
||||
* Use Quartz 2.0's native <code>JobDetailImpl</code> class or the new Quartz 2.0
|
||||
* builder API instead. Alternatively, switch to Spring's {@link SimpleTriggerFactoryBean}
|
||||
* which largely is a drop-in replacement for this class and its properties and
|
||||
* consistently works against Quartz 1.x as well as Quartz 2.0/2.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.02.2004
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setJobDetail
|
||||
* @see SchedulerFactoryBean#setTriggers
|
||||
* @see SchedulerFactoryBean#setJobDetails
|
||||
* @see CronTriggerBean
|
||||
*/
|
||||
public class SimpleTriggerBean extends SimpleTrigger
|
||||
implements JobDetailAwareTrigger, BeanNameAware, InitializingBean {
|
||||
|
||||
/** Constants for the SimpleTrigger class */
|
||||
private static final Constants constants = new Constants(SimpleTrigger.class);
|
||||
|
||||
|
||||
private long startDelay = 0;
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
private String beanName;
|
||||
|
||||
|
||||
public SimpleTriggerBean() {
|
||||
setRepeatCount(REPEAT_INDEFINITELY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Trigger only,
|
||||
* in contrast to objects in the JobDetail's data map.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
|
||||
getJobDataMap().putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the misfire instruction via the name of the corresponding
|
||||
* constant in the {@link org.quartz.SimpleTrigger} class.
|
||||
* Default is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>.
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
|
||||
* @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
|
||||
*/
|
||||
public void setMisfireInstructionName(String constantName) {
|
||||
setMisfireInstruction(constants.asNumber(constantName).intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a list of TriggerListener names for this job, referring to
|
||||
* non-global TriggerListeners registered with the Scheduler.
|
||||
* <p>A TriggerListener name always refers to the name returned
|
||||
* by the TriggerListener implementation.
|
||||
* @see SchedulerFactoryBean#setTriggerListeners
|
||||
* @see org.quartz.TriggerListener#getName
|
||||
*/
|
||||
public void setTriggerListenerNames(String[] names) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
addTriggerListener(names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the {@link #setStartTime start time} of the trigger.
|
||||
* <p>If the start delay is non-zero, it will <strong>always</strong>
|
||||
* take precedence over start time.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JobDetail that this trigger should be associated with.
|
||||
* <p>This is typically used with a bean reference if the JobDetail
|
||||
* is a Spring-managed bean. Alternatively, the trigger can also
|
||||
* be associated with a job by name and group.
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
*/
|
||||
public void setJobDetail(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail;
|
||||
}
|
||||
|
||||
public JobDetail getJobDetail() {
|
||||
return this.jobDetail;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws ParseException {
|
||||
if (getName() == null) {
|
||||
setName(this.beanName);
|
||||
}
|
||||
if (getGroup() == null) {
|
||||
setGroup(Scheduler.DEFAULT_GROUP);
|
||||
}
|
||||
if (getStartTime() == null) {
|
||||
setStartTime(new Date(System.currentTimeMillis() + this.startDelay));
|
||||
}
|
||||
if (this.jobDetail != null) {
|
||||
setJobName(this.jobDetail.getName());
|
||||
setJobGroup(this.jobDetail.getGroup());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SimpleTrigger;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.Constants;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A Spring {@link FactoryBean} for creating a Quartz {@link org.quartz.SimpleTrigger}
|
||||
* instance, supporting bean-style usage for trigger configuration.
|
||||
*
|
||||
* <p><code>SimpleTrigger(Impl)</code> itself is already a JavaBean but lacks sensible defaults.
|
||||
* This class uses the Spring bean name as job name, the Quartz default group ("DEFAULT")
|
||||
* as job group, the current time as start time, and indefinite repetition, if not specified.
|
||||
*
|
||||
* <p>This class will also register the trigger with the job name and group of
|
||||
* a given {@link org.quartz.JobDetail}. This allows {@link SchedulerFactoryBean}
|
||||
* to automatically register a trigger for the corresponding JobDetail,
|
||||
* instead of registering the JobDetail separately.
|
||||
*
|
||||
* <p><b>NOTE:</b> This FactoryBean works against both Quartz 1.x and Quartz 2.0/2.1,
|
||||
* in contrast to the older {@link SimpleTriggerBean} class.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setJobDetail
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
|
||||
* @see org.springframework.scheduling.quartz.CronTriggerBean
|
||||
*/
|
||||
public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, BeanNameAware, InitializingBean {
|
||||
|
||||
/** Constants for the SimpleTrigger class */
|
||||
private static final Constants constants = new Constants(SimpleTrigger.class);
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
private String group;
|
||||
|
||||
private JobDetail jobDetail;
|
||||
|
||||
private JobDataMap jobDataMap = new JobDataMap();
|
||||
|
||||
private Date startTime;
|
||||
|
||||
private long startDelay;
|
||||
|
||||
private long repeatInterval;
|
||||
|
||||
private int priority;
|
||||
|
||||
private int misfireInstruction;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private SimpleTrigger simpleTrigger;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the trigger's name.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the trigger's group.
|
||||
*/
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JobDetail that this trigger should be associated with.
|
||||
*/
|
||||
public void setJobDetail(JobDetail jobDetail) {
|
||||
this.jobDetail = jobDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the trigger's JobDataMap.
|
||||
* @see #setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataMap(JobDataMap jobDataMap) {
|
||||
this.jobDataMap = jobDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the trigger's JobDataMap.
|
||||
*/
|
||||
public JobDataMap getJobDataMap() {
|
||||
return this.jobDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register objects in the JobDataMap via a given Map.
|
||||
* <p>These objects will be available to this Trigger only,
|
||||
* in contrast to objects in the JobDetail's data map.
|
||||
* @param jobDataAsMap Map with String keys and any objects as values
|
||||
* (for example Spring-managed beans)
|
||||
* @see org.springframework.scheduling.quartz.JobDetailBean#setJobDataAsMap
|
||||
*/
|
||||
public void setJobDataAsMap(Map<String, ?> jobDataAsMap) {
|
||||
this.jobDataMap.putAll(jobDataAsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the start time of the trigger.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
Assert.state(startDelay >= 0, "Start delay cannot be negative.");
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the interval between execution times of this trigger.
|
||||
*/
|
||||
public void setRepeatInterval(long repeatInterval) {
|
||||
this.repeatInterval = repeatInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the priority of this trigger.
|
||||
*/
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a misfire instruction for this trigger.
|
||||
*/
|
||||
public void setMisfireInstruction(int misfireInstruction) {
|
||||
this.misfireInstruction = misfireInstruction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the misfire instruction via the name of the corresponding
|
||||
* constant in the {@link org.quartz.SimpleTrigger} class.
|
||||
* Default is <code>MISFIRE_INSTRUCTION_SMART_POLICY</code>.
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
|
||||
* @see org.quartz.SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
|
||||
* @see org.quartz.Trigger#MISFIRE_INSTRUCTION_SMART_POLICY
|
||||
*/
|
||||
public void setMisfireInstructionName(String constantName) {
|
||||
this.misfireInstruction = constants.asNumber(constantName).intValue();
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws ParseException {
|
||||
if (this.name == null) {
|
||||
this.name = this.beanName;
|
||||
}
|
||||
if (this.group == null) {
|
||||
this.group = Scheduler.DEFAULT_GROUP;
|
||||
}
|
||||
if (this.jobDetail != null) {
|
||||
this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail);
|
||||
}
|
||||
if (this.startDelay > 0) {
|
||||
this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
|
||||
}
|
||||
else if (this.startTime == null) {
|
||||
this.startTime = new Date();
|
||||
}
|
||||
|
||||
/*
|
||||
SimpleTriggerImpl sti = new SimpleTriggerImpl();
|
||||
sti.setName(this.name);
|
||||
sti.setGroup(this.group);
|
||||
sti.setJobKey(this.jobDetail.getKey());
|
||||
sti.setJobDataMap(this.jobDataMap);
|
||||
sti.setStartTime(this.startTime);
|
||||
sti.setRepeatInterval(this.repeatInterval);
|
||||
sti.setPriority(this.priority);
|
||||
sti.setMisfireInstruction(this.misfireInstruction);
|
||||
this.simpleTrigger = sti;
|
||||
*/
|
||||
|
||||
Class simpleTriggerClass;
|
||||
Method jobKeyMethod;
|
||||
try {
|
||||
simpleTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.SimpleTriggerImpl");
|
||||
jobKeyMethod = JobDetail.class.getMethod("getKey");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
simpleTriggerClass = SimpleTrigger.class;
|
||||
jobKeyMethod = null;
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("Incompatible Quartz version");
|
||||
}
|
||||
BeanWrapper bw = new BeanWrapperImpl(simpleTriggerClass);
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("name", this.name);
|
||||
pvs.add("group", this.group);
|
||||
if (jobKeyMethod != null) {
|
||||
pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail));
|
||||
}
|
||||
else {
|
||||
pvs.add("jobName", this.jobDetail.getName());
|
||||
pvs.add("jobGroup", this.jobDetail.getGroup());
|
||||
}
|
||||
pvs.add("jobDataMap", this.jobDataMap);
|
||||
pvs.add("startTime", this.startTime);
|
||||
pvs.add("repeatInterval", this.repeatInterval);
|
||||
pvs.add("repeatCount", -1);
|
||||
pvs.add("priority", this.priority);
|
||||
pvs.add("misfireInstruction", this.misfireInstruction);
|
||||
bw.setPropertyValues(pvs);
|
||||
this.simpleTrigger = (SimpleTrigger) bw.getWrappedInstance();
|
||||
}
|
||||
|
||||
|
||||
public SimpleTrigger getObject() {
|
||||
return this.simpleTrigger;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return SimpleTrigger.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.SchedulerContext;
|
||||
import org.quartz.spi.TriggerFiredBundle;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Subclass of {@link AdaptableJobFactory} that also supports Spring-style
|
||||
* dependency injection on bean properties. This is essentially the direct
|
||||
* equivalent of Spring's {@link QuartzJobBean} in the shape of a
|
||||
* Quartz 1.5 {@link org.quartz.spi.JobFactory}.
|
||||
*
|
||||
* <p>Applies scheduler context, job data map and trigger data map entries
|
||||
* as bean property values. If no matching bean property is found, the entry
|
||||
* is by default simply ignored. This is analogous to QuartzJobBean's behavior.
|
||||
*
|
||||
* <p>Compatible with Quartz 1.5+ as well as Quartz 2.0/2.1, as of Spring 3.1.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SchedulerFactoryBean#setJobFactory
|
||||
* @see QuartzJobBean
|
||||
*/
|
||||
public class SpringBeanJobFactory extends AdaptableJobFactory implements SchedulerContextAware {
|
||||
|
||||
private String[] ignoredUnknownProperties;
|
||||
|
||||
private SchedulerContext schedulerContext;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the unknown properties (not found in the bean) that should be ignored.
|
||||
* <p>Default is <code>null</code>, indicating that all unknown properties
|
||||
* should be ignored. Specify an empty array to throw an exception in case
|
||||
* of any unknown properties, or a list of property names that should be
|
||||
* ignored if there is no corresponding property found on the particular
|
||||
* job class (all other unknown properties will still trigger an exception).
|
||||
*/
|
||||
public void setIgnoredUnknownProperties(String[] ignoredUnknownProperties) {
|
||||
this.ignoredUnknownProperties = ignoredUnknownProperties;
|
||||
}
|
||||
|
||||
public void setSchedulerContext(SchedulerContext schedulerContext) {
|
||||
this.schedulerContext = schedulerContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the job instance, populating it with property values taken
|
||||
* from the scheduler context, job data map and trigger data map.
|
||||
*/
|
||||
@Override
|
||||
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
|
||||
Object job = super.createJobInstance(bundle);
|
||||
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
|
||||
if (isEligibleForPropertyPopulation(bw.getWrappedInstance())) {
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
if (this.schedulerContext != null) {
|
||||
pvs.addPropertyValues(this.schedulerContext);
|
||||
}
|
||||
pvs.addPropertyValues(getJobDetailDataMap(bundle));
|
||||
pvs.addPropertyValues(getTriggerDataMap(bundle));
|
||||
if (this.ignoredUnknownProperties != null) {
|
||||
for (String propName : this.ignoredUnknownProperties) {
|
||||
if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
|
||||
pvs.removePropertyValue(propName);
|
||||
}
|
||||
}
|
||||
bw.setPropertyValues(pvs);
|
||||
}
|
||||
else {
|
||||
bw.setPropertyValues(pvs, true);
|
||||
}
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given job object is eligible for having
|
||||
* its bean properties populated.
|
||||
* <p>The default implementation ignores {@link QuartzJobBean} instances,
|
||||
* which will inject bean properties themselves.
|
||||
* @param jobObject the job object to introspect
|
||||
* @see QuartzJobBean
|
||||
*/
|
||||
protected boolean isEligibleForPropertyPopulation(Object jobObject) {
|
||||
return (!(jobObject instanceof QuartzJobBean));
|
||||
}
|
||||
|
||||
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
|
||||
private JobDataMap getJobDetailDataMap(TriggerFiredBundle bundle) throws Exception {
|
||||
Method getJobDetail = bundle.getClass().getMethod("getJobDetail");
|
||||
Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle);
|
||||
Method getJobDataMap = jobDetail.getClass().getMethod("getJobDataMap");
|
||||
return (JobDataMap) ReflectionUtils.invokeMethod(getJobDataMap, jobDetail);
|
||||
}
|
||||
|
||||
// Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
|
||||
private JobDataMap getTriggerDataMap(TriggerFiredBundle bundle) throws Exception {
|
||||
Method getTrigger = bundle.getClass().getMethod("getTrigger");
|
||||
Object trigger = ReflectionUtils.invokeMethod(getTrigger, bundle);
|
||||
Method getJobDataMap = trigger.getClass().getMethod("getJobDataMap");
|
||||
return (JobDataMap) ReflectionUtils.invokeMethod(getJobDataMap, trigger);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for the open source scheduler
|
||||
* <a href="http://www.opensymphony.com/quartz">Quartz</a>,
|
||||
* allowing to set up Quartz Schedulers, JobDetails and
|
||||
* Triggers as beans in a Spring context. Also provides
|
||||
* convenience classes for implementing Quartz Jobs.
|
||||
*
|
||||
*/
|
||||
package org.springframework.scheduling.quartz;
|
||||
|
||||
@@ -1,423 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.freemarker;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import freemarker.cache.FileTemplateLoader;
|
||||
import freemarker.cache.MultiTemplateLoader;
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.SimpleHash;
|
||||
import freemarker.template.TemplateException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Factory that configures a FreeMarker Configuration. Can be used standalone, but
|
||||
* typically you will either use FreeMarkerConfigurationFactoryBean for preparing a
|
||||
* Configuration as bean reference, or FreeMarkerConfigurer for web views.
|
||||
*
|
||||
* <p>The optional "configLocation" property sets the location of a FreeMarker
|
||||
* properties file, within the current application. FreeMarker properties can be
|
||||
* overridden via "freemarkerSettings". All of these properties will be set by
|
||||
* calling FreeMarker's <code>Configuration.setSettings()</code> method and are
|
||||
* subject to constraints set by FreeMarker.
|
||||
*
|
||||
* <p>The "freemarkerVariables" property can be used to specify a Map of
|
||||
* shared variables that will be applied to the Configuration via the
|
||||
* <code>setAllSharedVariables()</code> method. Like <code>setSettings()</code>,
|
||||
* these entries are subject to FreeMarker constraints.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a "templateLoaderPath";
|
||||
* FreeMarker does not need any further configuration then.
|
||||
*
|
||||
* <p>Note: Spring's FreeMarker support requires FreeMarker 2.3 or higher.
|
||||
*
|
||||
* @author Darren Davison
|
||||
* @author Juergen Hoeller
|
||||
* @since 03.03.2004
|
||||
* @see #setConfigLocation
|
||||
* @see #setFreemarkerSettings
|
||||
* @see #setFreemarkerVariables
|
||||
* @see #setTemplateLoaderPath
|
||||
* @see #createConfiguration
|
||||
* @see FreeMarkerConfigurationFactoryBean
|
||||
* @see org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer
|
||||
* @see freemarker.template.Configuration
|
||||
*/
|
||||
public class FreeMarkerConfigurationFactory {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private Properties freemarkerSettings;
|
||||
|
||||
private Map<String, Object> freemarkerVariables;
|
||||
|
||||
private String defaultEncoding;
|
||||
|
||||
private final List<TemplateLoader> templateLoaders = new ArrayList<TemplateLoader>();
|
||||
|
||||
private List<TemplateLoader> preTemplateLoaders;
|
||||
|
||||
private List<TemplateLoader> postTemplateLoaders;
|
||||
|
||||
private String[] templateLoaderPaths;
|
||||
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private boolean preferFileSystemAccess = true;
|
||||
|
||||
|
||||
/**
|
||||
* Set the location of the FreeMarker config file.
|
||||
* Alternatively, you can specify all setting locally.
|
||||
* @see #setFreemarkerSettings
|
||||
* @see #setTemplateLoaderPath
|
||||
*/
|
||||
public void setConfigLocation(Resource resource) {
|
||||
configLocation = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties that contain well-known FreeMarker keys which will be
|
||||
* passed to FreeMarker's <code>Configuration.setSettings</code> method.
|
||||
* @see freemarker.template.Configuration#setSettings
|
||||
*/
|
||||
public void setFreemarkerSettings(Properties settings) {
|
||||
this.freemarkerSettings = settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Map that contains well-known FreeMarker objects which will be passed
|
||||
* to FreeMarker's <code>Configuration.setAllSharedVariables()</code> method.
|
||||
* @see freemarker.template.Configuration#setAllSharedVariables
|
||||
*/
|
||||
public void setFreemarkerVariables(Map<String, Object> variables) {
|
||||
this.freemarkerVariables = variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default encoding for the FreeMarker configuration.
|
||||
* If not specified, FreeMarker will use the platform file encoding.
|
||||
* <p>Used for template rendering unless there is an explicit encoding specified
|
||||
* for the rendering process (for example, on Spring's FreeMarkerView).
|
||||
* @see freemarker.template.Configuration#setDefaultEncoding
|
||||
* @see org.springframework.web.servlet.view.freemarker.FreeMarkerView#setEncoding
|
||||
*/
|
||||
public void setDefaultEncoding(String defaultEncoding) {
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* @deprecated as of Spring 2.0.1, in favor of the "preTemplateLoaders"
|
||||
* and "postTemplateLoaders" properties
|
||||
* @see #setPreTemplateLoaders
|
||||
* @see #setPostTemplateLoaders
|
||||
*/
|
||||
@Deprecated
|
||||
public void setTemplateLoaders(TemplateLoader[] templateLoaders) {
|
||||
if (templateLoaders != null) {
|
||||
this.templateLoaders.addAll(Arrays.asList(templateLoaders));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
* registered <i>before</i> the default template loaders that this factory
|
||||
* registers (such as loaders for specified "templateLoaderPaths" or any
|
||||
* loaders registered in {@link #postProcessTemplateLoaders}).
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPreTemplateLoaders(TemplateLoader[] preTemplateLoaders) {
|
||||
this.preTemplateLoaders = Arrays.asList(preTemplateLoaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders can be configured.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
* registered <i>after</i> the default template loaders that this factory
|
||||
* registers (such as loaders for specified "templateLoaderPaths" or any
|
||||
* loaders registered in {@link #postProcessTemplateLoaders}).
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPostTemplateLoaders(TemplateLoader[] postTemplateLoaders) {
|
||||
this.postTemplateLoaders = Arrays.asList(postTemplateLoaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Freemarker template loader path via a Spring resource location.
|
||||
* See the "templateLoaderPaths" property for details on path handling.
|
||||
* @see #setTemplateLoaderPaths
|
||||
*/
|
||||
public void setTemplateLoaderPath(String templateLoaderPath) {
|
||||
this.templateLoaderPaths = new String[] {templateLoaderPath};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set multiple Freemarker template loader paths via Spring resource locations.
|
||||
* <p>When populated via a String, standard URLs like "file:" and "classpath:"
|
||||
* pseudo URLs are supported, as understood by ResourceEditor. Allows for
|
||||
* relative paths when running in an ApplicationContext.
|
||||
* <p>Will define a path for the default FreeMarker template loader.
|
||||
* If a specified resource cannot be resolved to a <code>java.io.File</code>,
|
||||
* a generic SpringTemplateLoader will be used, without modification detection.
|
||||
* <p>To enforce the use of SpringTemplateLoader, i.e. to not resolve a path
|
||||
* as file system resource in any case, turn off the "preferFileSystemAccess"
|
||||
* flag. See the latter's javadoc for details.
|
||||
* <p>If you wish to specify your own list of TemplateLoaders, do not set this
|
||||
* property and instead use <code>setTemplateLoaders(List templateLoaders)</code>
|
||||
* @see org.springframework.core.io.ResourceEditor
|
||||
* @see org.springframework.context.ApplicationContext#getResource
|
||||
* @see freemarker.template.Configuration#setDirectoryForTemplateLoading
|
||||
* @see SpringTemplateLoader
|
||||
* @see #setTemplateLoaders
|
||||
*/
|
||||
public void setTemplateLoaderPaths(String[] templateLoaderPaths) {
|
||||
this.templateLoaderPaths = templateLoaderPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Spring ResourceLoader to use for loading FreeMarker template files.
|
||||
* The default is DefaultResourceLoader. Will get overridden by the
|
||||
* ApplicationContext if running in a context.
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Spring ResourceLoader to use for loading FreeMarker template files.
|
||||
*/
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to prefer file system access for template loading.
|
||||
* File system access enables hot detection of template changes.
|
||||
* <p>If this is enabled, FreeMarkerConfigurationFactory will try to resolve
|
||||
* the specified "templateLoaderPath" as file system resource (which will work
|
||||
* for expanded class path resources and ServletContext resources too).
|
||||
* <p>Default is "true". Turn this off to always load via SpringTemplateLoader
|
||||
* (i.e. as stream, without hot detection of template changes), which might
|
||||
* be necessary if some of your templates reside in an expanded classes
|
||||
* directory while others reside in jar files.
|
||||
* @see #setTemplateLoaderPath
|
||||
*/
|
||||
public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
|
||||
this.preferFileSystemAccess = preferFileSystemAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to prefer file system access for template loading.
|
||||
*/
|
||||
protected boolean isPreferFileSystemAccess() {
|
||||
return preferFileSystemAccess;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the FreeMarker Configuration and return it.
|
||||
* @return the FreeMarker Configuration object
|
||||
* @throws IOException if the config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
*/
|
||||
public Configuration createConfiguration() throws IOException, TemplateException {
|
||||
Configuration config = newConfiguration();
|
||||
Properties props = new Properties();
|
||||
|
||||
// Load config file if specified.
|
||||
if (this.configLocation != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading FreeMarker configuration from " + this.configLocation);
|
||||
}
|
||||
PropertiesLoaderUtils.fillProperties(props, this.configLocation);
|
||||
}
|
||||
|
||||
// Merge local properties if specified.
|
||||
if (this.freemarkerSettings != null) {
|
||||
props.putAll(this.freemarkerSettings);
|
||||
}
|
||||
|
||||
// FreeMarker will only accept known keys in its setSettings and
|
||||
// setAllSharedVariables methods.
|
||||
if (!props.isEmpty()) {
|
||||
config.setSettings(props);
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(this.freemarkerVariables)) {
|
||||
config.setAllSharedVariables(new SimpleHash(this.freemarkerVariables, config.getObjectWrapper()));
|
||||
}
|
||||
|
||||
if (this.defaultEncoding != null) {
|
||||
config.setDefaultEncoding(this.defaultEncoding);
|
||||
}
|
||||
|
||||
// Register template loaders that are supposed to kick in early.
|
||||
if (this.preTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.preTemplateLoaders);
|
||||
}
|
||||
|
||||
// Register default template loaders.
|
||||
if (this.templateLoaderPaths != null) {
|
||||
for (String path : this.templateLoaderPaths) {
|
||||
this.templateLoaders.add(getTemplateLoaderForPath(path));
|
||||
}
|
||||
}
|
||||
postProcessTemplateLoaders(this.templateLoaders);
|
||||
|
||||
// Register template loaders that are supposed to kick in late.
|
||||
if (this.postTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.postTemplateLoaders);
|
||||
}
|
||||
|
||||
TemplateLoader loader = getAggregateTemplateLoader(this.templateLoaders);
|
||||
if (loader != null) {
|
||||
config.setTemplateLoader(loader);
|
||||
}
|
||||
|
||||
postProcessConfiguration(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new Configuration object. Subclasses can override this for
|
||||
* custom initialization, or for using a mock object for testing.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* @return the Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
* @see #createConfiguration()
|
||||
*/
|
||||
protected Configuration newConfiguration() throws IOException, TemplateException {
|
||||
return new Configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a FreeMarker TemplateLoader for the given path.
|
||||
* <p>Default implementation creates either a FileTemplateLoader or
|
||||
* a SpringTemplateLoader.
|
||||
* @param templateLoaderPath the path to load templates from
|
||||
* @return an appropriate TemplateLoader
|
||||
* @see freemarker.cache.FileTemplateLoader
|
||||
* @see SpringTemplateLoader
|
||||
*/
|
||||
protected TemplateLoader getTemplateLoaderForPath(String templateLoaderPath) {
|
||||
if (isPreferFileSystemAccess()) {
|
||||
// Try to load via the file system, fall back to SpringTemplateLoader
|
||||
// (for hot detection of template changes, if possible).
|
||||
try {
|
||||
Resource path = getResourceLoader().getResource(templateLoaderPath);
|
||||
File file = path.getFile(); // will fail if not resolvable in the file system
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Template loader path [" + path + "] resolved to file path [" + file.getAbsolutePath() + "]");
|
||||
}
|
||||
return new FileTemplateLoader(file);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cannot resolve template loader path [" + templateLoaderPath +
|
||||
"] to [java.io.File]: using SpringTemplateLoader as fallback", ex);
|
||||
}
|
||||
return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Always load via SpringTemplateLoader (without hot detection of template changes).
|
||||
logger.debug("File system access not preferred: using SpringTemplateLoader");
|
||||
return new SpringTemplateLoader(getResourceLoader(), templateLoaderPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overridden by subclasses that want to to register custom
|
||||
* TemplateLoader instances after this factory created its default
|
||||
* template loaders.
|
||||
* <p>Called by <code>createConfiguration()</code>. Note that specified
|
||||
* "postTemplateLoaders" will be registered <i>after</i> any loaders
|
||||
* registered by this callback; as a consequence, they are are <i>not</i>
|
||||
* included in the given List.
|
||||
* @param templateLoaders the current List of TemplateLoader instances,
|
||||
* to be modified by a subclass
|
||||
* @see #createConfiguration()
|
||||
* @see #setPostTemplateLoaders
|
||||
*/
|
||||
protected void postProcessTemplateLoaders(List<TemplateLoader> templateLoaders) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a TemplateLoader based on the given TemplateLoader list.
|
||||
* If more than one TemplateLoader has been registered, a FreeMarker
|
||||
* MultiTemplateLoader needs to be created.
|
||||
* @param templateLoaders the final List of TemplateLoader instances
|
||||
* @return the aggregate TemplateLoader
|
||||
*/
|
||||
protected TemplateLoader getAggregateTemplateLoader(List<TemplateLoader> templateLoaders) {
|
||||
int loaderCount = templateLoaders.size();
|
||||
switch (loaderCount) {
|
||||
case 0:
|
||||
logger.info("No FreeMarker TemplateLoaders specified");
|
||||
return null;
|
||||
case 1:
|
||||
return templateLoaders.get(0);
|
||||
default:
|
||||
TemplateLoader[] loaders = templateLoaders.toArray(new TemplateLoader[loaderCount]);
|
||||
return new MultiTemplateLoader(loaders);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overridden by subclasses that want to to perform custom
|
||||
* post-processing of the Configuration object after this factory
|
||||
* performed its default initialization.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* @param config the current Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
* @see #createConfiguration()
|
||||
*/
|
||||
protected void postProcessConfiguration(Configuration config) throws IOException, TemplateException {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.TemplateException;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
||||
/**
|
||||
* Factory bean that creates a FreeMarker Configuration and provides it as
|
||||
* bean reference. This bean is intended for any kind of usage of FreeMarker
|
||||
* in application code, e.g. for generating email content. For web views,
|
||||
* FreeMarkerConfigurer is used to set up a FreeMarkerConfigurationFactory.
|
||||
*
|
||||
* The simplest way to use this class is to specify just a "templateLoaderPath";
|
||||
* you do not need any further configuration then. For example, in a web
|
||||
* application context:
|
||||
*
|
||||
* <pre class="code"> <bean id="freemarkerConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
|
||||
* <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
|
||||
* </bean></pre>
|
||||
|
||||
* See the base class FreeMarkerConfigurationFactory for configuration details.
|
||||
*
|
||||
* <p>Note: Spring's FreeMarker support requires FreeMarker 2.3 or higher.
|
||||
*
|
||||
* @author Darren Davison
|
||||
* @since 03.03.2004
|
||||
* @see #setConfigLocation
|
||||
* @see #setFreemarkerSettings
|
||||
* @see #setTemplateLoaderPath
|
||||
* @see org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer
|
||||
*/
|
||||
public class FreeMarkerConfigurationFactoryBean extends FreeMarkerConfigurationFactory
|
||||
implements FactoryBean<Configuration>, InitializingBean, ResourceLoaderAware {
|
||||
|
||||
private Configuration configuration;
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws IOException, TemplateException {
|
||||
this.configuration = createConfiguration();
|
||||
}
|
||||
|
||||
|
||||
public Configuration getObject() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
public Class<? extends Configuration> getObjectType() {
|
||||
return Configuration.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
|
||||
/**
|
||||
* Utility class for working with FreeMarker.
|
||||
* Provides convenience methods to process a FreeMarker template with a model.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.03.2004
|
||||
*/
|
||||
public abstract class FreeMarkerTemplateUtils {
|
||||
|
||||
/**
|
||||
* Process the specified FreeMarker template with the given model and write
|
||||
* the result to the given Writer.
|
||||
* <p>When using this method to prepare a text for a mail to be sent with Spring's
|
||||
* mail support, consider wrapping IO/TemplateException in MailPreparationException.
|
||||
* @param model the model object, typically a Map that contains model names
|
||||
* as keys and model objects as values
|
||||
* @return the result as String
|
||||
* @throws IOException if the template wasn't found or couldn't be read
|
||||
* @throws freemarker.template.TemplateException if rendering failed
|
||||
* @see org.springframework.mail.MailPreparationException
|
||||
*/
|
||||
public static String processTemplateIntoString(Template template, Object model)
|
||||
throws IOException, TemplateException {
|
||||
StringWriter result = new StringWriter();
|
||||
template.process(model, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.freemarker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
|
||||
import freemarker.cache.TemplateLoader;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* FreeMarker TemplateLoader adapter that loads via a Spring ResourceLoader.
|
||||
* Used by FreeMarkerConfigurationFactory for any resource loader path that
|
||||
* cannot be resolved to a java.io.File.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.03.2004
|
||||
* @see FreeMarkerConfigurationFactory#setTemplateLoaderPath
|
||||
* @see freemarker.template.Configuration#setDirectoryForTemplateLoading
|
||||
*/
|
||||
public class SpringTemplateLoader implements TemplateLoader {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final String templateLoaderPath;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SpringTemplateLoader.
|
||||
* @param resourceLoader the Spring ResourceLoader to use
|
||||
* @param templateLoaderPath the template loader path to use
|
||||
*/
|
||||
public SpringTemplateLoader(ResourceLoader resourceLoader, String templateLoaderPath) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
if (!templateLoaderPath.endsWith("/")) {
|
||||
templateLoaderPath += "/";
|
||||
}
|
||||
this.templateLoaderPath = templateLoaderPath;
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("SpringTemplateLoader for FreeMarker: using resource loader [" + this.resourceLoader +
|
||||
"] and template loader path [" + this.templateLoaderPath + "]");
|
||||
}
|
||||
}
|
||||
|
||||
public Object findTemplateSource(String name) throws IOException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for FreeMarker template with name [" + name + "]");
|
||||
}
|
||||
Resource resource = this.resourceLoader.getResource(this.templateLoaderPath + name);
|
||||
return (resource.exists() ? resource : null);
|
||||
}
|
||||
|
||||
public Reader getReader(Object templateSource, String encoding) throws IOException {
|
||||
Resource resource = (Resource) templateSource;
|
||||
try {
|
||||
return new InputStreamReader(resource.getInputStream(), encoding);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not find FreeMarker template: " + resource);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public long getLastModified(Object templateSource) {
|
||||
Resource resource = (Resource) templateSource;
|
||||
try {
|
||||
return resource.lastModified();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not obtain last-modified timestamp for FreeMarker template in " +
|
||||
resource + ": " + ex);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void closeTemplateSource(Object templateSource) throws IOException {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for setting up
|
||||
* <a href="http://www.freemarker.org">FreeMarker</a>
|
||||
* within a Spring application context.
|
||||
*
|
||||
*/
|
||||
package org.springframework.ui.freemarker;
|
||||
|
||||
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.jasperreports;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sf.jasperreports.engine.JRDataSource;
|
||||
import net.sf.jasperreports.engine.JRException;
|
||||
import net.sf.jasperreports.engine.JRExporter;
|
||||
import net.sf.jasperreports.engine.JRExporterParameter;
|
||||
import net.sf.jasperreports.engine.JasperFillManager;
|
||||
import net.sf.jasperreports.engine.JasperPrint;
|
||||
import net.sf.jasperreports.engine.JasperReport;
|
||||
import net.sf.jasperreports.engine.data.JRBeanArrayDataSource;
|
||||
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
|
||||
import net.sf.jasperreports.engine.export.JRCsvExporter;
|
||||
import net.sf.jasperreports.engine.export.JRHtmlExporter;
|
||||
import net.sf.jasperreports.engine.export.JRPdfExporter;
|
||||
import net.sf.jasperreports.engine.export.JRXlsExporter;
|
||||
|
||||
/**
|
||||
* Utility methods for working with JasperReports. Provides a set of convenience
|
||||
* methods for generating reports in a CSV, HTML, PDF and XLS formats.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.3
|
||||
*/
|
||||
public abstract class JasperReportsUtils {
|
||||
|
||||
/**
|
||||
* Convert the given report data value to a <code>JRDataSource</code>.
|
||||
* <p>In the default implementation, a <code>JRDataSource</code>,
|
||||
* <code>java.util.Collection</code> or object array is detected.
|
||||
* The latter are converted to <code>JRBeanCollectionDataSource</code>
|
||||
* or <code>JRBeanArrayDataSource</code>, respectively.
|
||||
* @param value the report data value to convert
|
||||
* @return the JRDataSource (never <code>null</code>)
|
||||
* @throws IllegalArgumentException if the value could not be converted
|
||||
* @see net.sf.jasperreports.engine.JRDataSource
|
||||
* @see net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
|
||||
* @see net.sf.jasperreports.engine.data.JRBeanArrayDataSource
|
||||
*/
|
||||
public static JRDataSource convertReportData(Object value) throws IllegalArgumentException {
|
||||
if (value instanceof JRDataSource) {
|
||||
return (JRDataSource) value;
|
||||
}
|
||||
else if (value instanceof Collection) {
|
||||
return new JRBeanCollectionDataSource((Collection) value);
|
||||
}
|
||||
else if (value instanceof Object[]) {
|
||||
return new JRBeanArrayDataSource((Object[]) value);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Value [" + value + "] cannot be converted to a JRDataSource");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the supplied <code>JasperPrint</code> instance using the
|
||||
* supplied <code>JRAbstractExporter</code> instance and write the results
|
||||
* to the supplied <code>Writer</code>.
|
||||
* <p>Make sure that the <code>JRAbstractExporter</code> implementation
|
||||
* you supply is capable of writing to a <code>Writer</code>.
|
||||
* @param exporter the <code>JRAbstractExporter</code> to use to render the report
|
||||
* @param print the <code>JasperPrint</code> instance to render
|
||||
* @param writer the <code>Writer</code> to write the result to
|
||||
* @throws JRException if rendering failed
|
||||
*/
|
||||
public static void render(JRExporter exporter, JasperPrint print, Writer writer)
|
||||
throws JRException {
|
||||
|
||||
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
|
||||
exporter.setParameter(JRExporterParameter.OUTPUT_WRITER, writer);
|
||||
exporter.exportReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the supplied <code>JasperPrint</code> instance using the
|
||||
* supplied <code>JRAbstractExporter</code> instance and write the results
|
||||
* to the supplied <code>OutputStream</code>.
|
||||
* <p>Make sure that the <code>JRAbstractExporter</code> implementation you
|
||||
* supply is capable of writing to a <code>OutputStream</code>.
|
||||
* @param exporter the <code>JRAbstractExporter</code> to use to render the report
|
||||
* @param print the <code>JasperPrint</code> instance to render
|
||||
* @param outputStream the <code>OutputStream</code> to write the result to
|
||||
* @throws JRException if rendering failed
|
||||
*/
|
||||
public static void render(JRExporter exporter, JasperPrint print, OutputStream outputStream)
|
||||
throws JRException {
|
||||
|
||||
exporter.setParameter(JRExporterParameter.JASPER_PRINT, print);
|
||||
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream);
|
||||
exporter.exportReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in CSV format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsCsv(JasperReport report, Map parameters, Object reportData, Writer writer)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRCsvExporter(), print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in CSV format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsCsv(JasperReport report, Map parameters, Object reportData, Writer writer,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRCsvExporter exporter = new JRCsvExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in HTML format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsHtml(JasperReport report, Map parameters, Object reportData, Writer writer)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRHtmlExporter(), print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in HTML format using the supplied report data.
|
||||
* Writes the results to the supplied <code>Writer</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param writer the <code>Writer</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsHtml(JasperReport report, Map parameters, Object reportData, Writer writer,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRHtmlExporter exporter = new JRHtmlExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in PDF format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsPdf(JasperReport report, Map parameters, Object reportData, OutputStream stream)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRPdfExporter(), print, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in PDF format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsPdf(JasperReport report, Map parameters, Object reportData, OutputStream stream,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRPdfExporter exporter = new JRPdfExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in XLS format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsXls(JasperReport report, Map parameters, Object reportData, OutputStream stream)
|
||||
throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
render(new JRXlsExporter(), print, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a report in XLS format using the supplied report data.
|
||||
* Writes the results to the supplied <code>OutputStream</code>.
|
||||
* @param report the <code>JasperReport</code> instance to render
|
||||
* @param parameters the parameters to use for rendering
|
||||
* @param stream the <code>OutputStream</code> to write the rendered report to
|
||||
* @param reportData a <code>JRDataSource</code>, <code>java.util.Collection</code>
|
||||
* or object array (converted accordingly), representing the report data to read
|
||||
* fields from
|
||||
* @param exporterParameters a {@link Map} of {@link JRExporterParameter exporter parameters}
|
||||
* @throws JRException if rendering failed
|
||||
* @see #convertReportData
|
||||
*/
|
||||
public static void renderAsXls(JasperReport report, Map parameters, Object reportData, OutputStream stream,
|
||||
Map exporterParameters) throws JRException {
|
||||
|
||||
JasperPrint print = JasperFillManager.fillReport(report, parameters, convertReportData(reportData));
|
||||
JRXlsExporter exporter = new JRXlsExporter();
|
||||
exporter.setParameters(exporterParameters);
|
||||
render(exporter, print, stream);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for
|
||||
* <a href="http://jasperreports.sourceforge.net">JasperReports</a>.
|
||||
*
|
||||
*/
|
||||
package org.springframework.ui.jasperreports;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.velocity;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.runtime.RuntimeServices;
|
||||
import org.apache.velocity.runtime.log.LogSystem;
|
||||
|
||||
/**
|
||||
* Velocity LogSystem implementation for Jakarta Commons Logging.
|
||||
* Used by VelocityConfigurer to redirect log output.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 07.08.2003
|
||||
* @see VelocityEngineFactoryBean
|
||||
*/
|
||||
public class CommonsLoggingLogSystem implements LogSystem {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(VelocityEngine.class);
|
||||
|
||||
public void init(RuntimeServices runtimeServices) {
|
||||
}
|
||||
|
||||
public void logVelocityMessage(int type, String msg) {
|
||||
switch (type) {
|
||||
case ERROR_ID:
|
||||
logger.error(msg);
|
||||
break;
|
||||
case WARN_ID:
|
||||
logger.warn(msg);
|
||||
break;
|
||||
case INFO_ID:
|
||||
logger.info(msg);
|
||||
break;
|
||||
case DEBUG_ID:
|
||||
logger.debug(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.velocity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.collections.ExtendedProperties;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.exception.ResourceNotFoundException;
|
||||
import org.apache.velocity.runtime.resource.Resource;
|
||||
import org.apache.velocity.runtime.resource.loader.ResourceLoader;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Velocity ResourceLoader adapter that loads via a Spring ResourceLoader.
|
||||
* Used by VelocityEngineFactory for any resource loader path that cannot
|
||||
* be resolved to a <code>java.io.File</code>.
|
||||
*
|
||||
* <p>Note that this loader does not allow for modification detection:
|
||||
* Use Velocity's default FileResourceLoader for <code>java.io.File</code>
|
||||
* resources.
|
||||
*
|
||||
* <p>Expects "spring.resource.loader" and "spring.resource.loader.path"
|
||||
* application attributes in the Velocity runtime: the former of type
|
||||
* <code>org.springframework.core.io.ResourceLoader</code>, the latter a String.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 14.03.2004
|
||||
* @see VelocityEngineFactory#setResourceLoaderPath
|
||||
* @see org.springframework.core.io.ResourceLoader
|
||||
* @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
|
||||
*/
|
||||
public class SpringResourceLoader extends ResourceLoader {
|
||||
|
||||
public static final String NAME = "spring";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER_CLASS = "spring.resource.loader.class";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER_CACHE = "spring.resource.loader.cache";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER = "spring.resource.loader";
|
||||
|
||||
public static final String SPRING_RESOURCE_LOADER_PATH = "spring.resource.loader.path";
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private org.springframework.core.io.ResourceLoader resourceLoader;
|
||||
|
||||
private String[] resourceLoaderPaths;
|
||||
|
||||
|
||||
@Override
|
||||
public void init(ExtendedProperties configuration) {
|
||||
this.resourceLoader = (org.springframework.core.io.ResourceLoader)
|
||||
this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER);
|
||||
String resourceLoaderPath = (String) this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER_PATH);
|
||||
if (this.resourceLoader == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"'resourceLoader' application attribute must be present for SpringResourceLoader");
|
||||
}
|
||||
if (resourceLoaderPath == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"'resourceLoaderPath' application attribute must be present for SpringResourceLoader");
|
||||
}
|
||||
this.resourceLoaderPaths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath);
|
||||
for (int i = 0; i < this.resourceLoaderPaths.length; i++) {
|
||||
String path = this.resourceLoaderPaths[i];
|
||||
if (!path.endsWith("/")) {
|
||||
this.resourceLoaderPaths[i] = path + "/";
|
||||
}
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("SpringResourceLoader for Velocity: using resource loader [" + this.resourceLoader +
|
||||
"] and resource loader paths " + Arrays.asList(this.resourceLoaderPaths));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceStream(String source) throws ResourceNotFoundException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Looking for Velocity resource with name [" + source + "]");
|
||||
}
|
||||
for (int i = 0; i < this.resourceLoaderPaths.length; i++) {
|
||||
org.springframework.core.io.Resource resource =
|
||||
this.resourceLoader.getResource(this.resourceLoaderPaths[i] + source);
|
||||
try {
|
||||
return resource.getInputStream();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not find Velocity resource: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ResourceNotFoundException(
|
||||
"Could not find resource [" + source + "] in Spring resource loader path");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSourceModified(Resource resource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified(Resource resource) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.velocity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.exception.VelocityException;
|
||||
import org.apache.velocity.runtime.RuntimeConstants;
|
||||
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Factory that configures a VelocityEngine. Can be used standalone,
|
||||
* but typically you will either use {@link VelocityEngineFactoryBean}
|
||||
* for preparing a VelocityEngine as bean reference, or
|
||||
* {@link org.springframework.web.servlet.view.velocity.VelocityConfigurer}
|
||||
* for web views.
|
||||
*
|
||||
* <p>The optional "configLocation" property sets the location of the Velocity
|
||||
* properties file, within the current application. Velocity properties can be
|
||||
* overridden via "velocityProperties", or even completely specified locally,
|
||||
* avoiding the need for an external properties file.
|
||||
*
|
||||
* <p>The "resourceLoaderPath" property can be used to specify the Velocity
|
||||
* resource loader path via Spring's Resource abstraction, possibly relative
|
||||
* to the Spring application context.
|
||||
*
|
||||
* <p>If "overrideLogging" is true (the default), the VelocityEngine will be
|
||||
* configured to log via Commons Logging, that is, using the Spring-provided
|
||||
* {@link CommonsLoggingLogSystem} as log system.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a
|
||||
* {@link #setResourceLoaderPath(String) "resourceLoaderPath"}; the
|
||||
* VelocityEngine typically then does not need any further configuration.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @see #setConfigLocation
|
||||
* @see #setVelocityProperties
|
||||
* @see #setResourceLoaderPath
|
||||
* @see #setOverrideLogging
|
||||
* @see #createVelocityEngine
|
||||
* @see CommonsLoggingLogSystem
|
||||
* @see VelocityEngineFactoryBean
|
||||
* @see org.springframework.web.servlet.view.velocity.VelocityConfigurer
|
||||
* @see org.apache.velocity.app.VelocityEngine
|
||||
*/
|
||||
public class VelocityEngineFactory {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private final Map<String, Object> velocityProperties = new HashMap<String, Object>();
|
||||
|
||||
private String resourceLoaderPath;
|
||||
|
||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
private boolean preferFileSystemAccess = true;
|
||||
|
||||
private boolean overrideLogging = true;
|
||||
|
||||
|
||||
/**
|
||||
* Set the location of the Velocity config file.
|
||||
* Alternatively, you can specify all properties locally.
|
||||
* @see #setVelocityProperties
|
||||
* @see #setResourceLoaderPath
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocation = configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Velocity properties, like "file.resource.loader.path".
|
||||
* Can be used to override values in a Velocity config file,
|
||||
* or to specify all necessary properties locally.
|
||||
* <p>Note that the Velocity resource loader path also be set to any
|
||||
* Spring resource location via the "resourceLoaderPath" property.
|
||||
* Setting it here is just necessary when using a non-file-based
|
||||
* resource loader.
|
||||
* @see #setVelocityPropertiesMap
|
||||
* @see #setConfigLocation
|
||||
* @see #setResourceLoaderPath
|
||||
*/
|
||||
public void setVelocityProperties(Properties velocityProperties) {
|
||||
CollectionUtils.mergePropertiesIntoMap(velocityProperties, this.velocityProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Velocity properties as Map, to allow for non-String values
|
||||
* like "ds.resource.loader.instance".
|
||||
* @see #setVelocityProperties
|
||||
*/
|
||||
public void setVelocityPropertiesMap(Map<String, Object> velocityPropertiesMap) {
|
||||
if (velocityPropertiesMap != null) {
|
||||
this.velocityProperties.putAll(velocityPropertiesMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Velocity resource loader path via a Spring resource location.
|
||||
* Accepts multiple locations in Velocity's comma-separated path style.
|
||||
* <p>When populated via a String, standard URLs like "file:" and "classpath:"
|
||||
* pseudo URLs are supported, as understood by ResourceLoader. Allows for
|
||||
* relative paths when running in an ApplicationContext.
|
||||
* <p>Will define a path for the default Velocity resource loader with the name
|
||||
* "file". If the specified resource cannot be resolved to a <code>java.io.File</code>,
|
||||
* a generic SpringResourceLoader will be used under the name "spring", without
|
||||
* modification detection.
|
||||
* <p>Note that resource caching will be enabled in any case. With the file
|
||||
* resource loader, the last-modified timestamp will be checked on access to
|
||||
* detect changes. With SpringResourceLoader, the resource will be cached
|
||||
* forever (for example for class path resources).
|
||||
* <p>To specify a modification check interval for files, use Velocity's
|
||||
* standard "file.resource.loader.modificationCheckInterval" property. By default,
|
||||
* the file timestamp is checked on every access (which is surprisingly fast).
|
||||
* Of course, this just applies when loading resources from the file system.
|
||||
* <p>To enforce the use of SpringResourceLoader, i.e. to not resolve a path
|
||||
* as file system resource in any case, turn off the "preferFileSystemAccess"
|
||||
* flag. See the latter's javadoc for details.
|
||||
* @see #setResourceLoader
|
||||
* @see #setVelocityProperties
|
||||
* @see #setPreferFileSystemAccess
|
||||
* @see SpringResourceLoader
|
||||
* @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
|
||||
*/
|
||||
public void setResourceLoaderPath(String resourceLoaderPath) {
|
||||
this.resourceLoaderPath = resourceLoaderPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Spring ResourceLoader to use for loading Velocity template files.
|
||||
* The default is DefaultResourceLoader. Will get overridden by the
|
||||
* ApplicationContext if running in a context.
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
* @see org.springframework.context.ApplicationContext
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Spring ResourceLoader to use for loading Velocity template files.
|
||||
*/
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return this.resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to prefer file system access for template loading.
|
||||
* File system access enables hot detection of template changes.
|
||||
* <p>If this is enabled, VelocityEngineFactory will try to resolve the
|
||||
* specified "resourceLoaderPath" as file system resource (which will work
|
||||
* for expanded class path resources and ServletContext resources too).
|
||||
* <p>Default is "true". Turn this off to always load via SpringResourceLoader
|
||||
* (i.e. as stream, without hot detection of template changes), which might
|
||||
* be necessary if some of your templates reside in an expanded classes
|
||||
* directory while others reside in jar files.
|
||||
* @see #setResourceLoaderPath
|
||||
*/
|
||||
public void setPreferFileSystemAccess(boolean preferFileSystemAccess) {
|
||||
this.preferFileSystemAccess = preferFileSystemAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to prefer file system access for template loading.
|
||||
*/
|
||||
protected boolean isPreferFileSystemAccess() {
|
||||
return this.preferFileSystemAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether Velocity should log via Commons Logging, i.e. whether Velocity's
|
||||
* log system should be set to CommonsLoggingLogSystem. Default value is true.
|
||||
* @see CommonsLoggingLogSystem
|
||||
*/
|
||||
public void setOverrideLogging(boolean overrideLogging) {
|
||||
this.overrideLogging = overrideLogging;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the VelocityEngine instance and return it.
|
||||
* @return the VelocityEngine instance
|
||||
* @throws IOException if the config file wasn't found
|
||||
* @throws VelocityException on Velocity initialization failure
|
||||
*/
|
||||
public VelocityEngine createVelocityEngine() throws IOException, VelocityException {
|
||||
VelocityEngine velocityEngine = newVelocityEngine();
|
||||
Map<String, Object> props = new HashMap<String, Object>();
|
||||
|
||||
// Load config file if set.
|
||||
if (this.configLocation != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading Velocity config from [" + this.configLocation + "]");
|
||||
}
|
||||
CollectionUtils.mergePropertiesIntoMap(PropertiesLoaderUtils.loadProperties(this.configLocation), props);
|
||||
}
|
||||
|
||||
// Merge local properties if set.
|
||||
if (!this.velocityProperties.isEmpty()) {
|
||||
props.putAll(this.velocityProperties);
|
||||
}
|
||||
|
||||
// Set a resource loader path, if required.
|
||||
if (this.resourceLoaderPath != null) {
|
||||
initVelocityResourceLoader(velocityEngine, this.resourceLoaderPath);
|
||||
}
|
||||
|
||||
// Log via Commons Logging?
|
||||
if (this.overrideLogging) {
|
||||
velocityEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new CommonsLoggingLogSystem());
|
||||
}
|
||||
|
||||
// Apply properties to VelocityEngine.
|
||||
for (Map.Entry<String, Object> entry : props.entrySet()) {
|
||||
velocityEngine.setProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
postProcessVelocityEngine(velocityEngine);
|
||||
|
||||
try {
|
||||
// Perform actual initialization.
|
||||
velocityEngine.init();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (VelocityException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
|
||||
throw new VelocityException(ex.toString());
|
||||
}
|
||||
|
||||
return velocityEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new VelocityEngine. Subclasses can override this for
|
||||
* custom initialization, or for using a mock object for testing.
|
||||
* <p>Called by <code>createVelocityEngine()</code>.
|
||||
* @return the VelocityEngine instance
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws VelocityException on Velocity initialization failure
|
||||
* @see #createVelocityEngine()
|
||||
*/
|
||||
protected VelocityEngine newVelocityEngine() throws IOException, VelocityException {
|
||||
return new VelocityEngine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a Velocity resource loader for the given VelocityEngine:
|
||||
* either a standard Velocity FileResourceLoader or a SpringResourceLoader.
|
||||
* <p>Called by <code>createVelocityEngine()</code>.
|
||||
* @param velocityEngine the VelocityEngine to configure
|
||||
* @param resourceLoaderPath the path to load Velocity resources from
|
||||
* @see org.apache.velocity.runtime.resource.loader.FileResourceLoader
|
||||
* @see SpringResourceLoader
|
||||
* @see #initSpringResourceLoader
|
||||
* @see #createVelocityEngine()
|
||||
*/
|
||||
protected void initVelocityResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) {
|
||||
if (isPreferFileSystemAccess()) {
|
||||
// Try to load via the file system, fall back to SpringResourceLoader
|
||||
// (for hot detection of template changes, if possible).
|
||||
try {
|
||||
StringBuilder resolvedPath = new StringBuilder();
|
||||
String[] paths = StringUtils.commaDelimitedListToStringArray(resourceLoaderPath);
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
String path = paths[i];
|
||||
Resource resource = getResourceLoader().getResource(path);
|
||||
File file = resource.getFile(); // will fail if not resolvable in the file system
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resource loader path [" + path + "] resolved to file [" + file.getAbsolutePath() + "]");
|
||||
}
|
||||
resolvedPath.append(file.getAbsolutePath());
|
||||
if (i < paths.length - 1) {
|
||||
resolvedPath.append(',');
|
||||
}
|
||||
}
|
||||
velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file");
|
||||
velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_CACHE, "true");
|
||||
velocityEngine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, resolvedPath.toString());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Cannot resolve resource loader path [" + resourceLoaderPath +
|
||||
"] to [java.io.File]: using SpringResourceLoader", ex);
|
||||
}
|
||||
initSpringResourceLoader(velocityEngine, resourceLoaderPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Always load via SpringResourceLoader
|
||||
// (without hot detection of template changes).
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("File system access not preferred: using SpringResourceLoader");
|
||||
}
|
||||
initSpringResourceLoader(velocityEngine, resourceLoaderPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a SpringResourceLoader for the given VelocityEngine.
|
||||
* <p>Called by <code>initVelocityResourceLoader</code>.
|
||||
* @param velocityEngine the VelocityEngine to configure
|
||||
* @param resourceLoaderPath the path to load Velocity resources from
|
||||
* @see SpringResourceLoader
|
||||
* @see #initVelocityResourceLoader
|
||||
*/
|
||||
protected void initSpringResourceLoader(VelocityEngine velocityEngine, String resourceLoaderPath) {
|
||||
velocityEngine.setProperty(
|
||||
RuntimeConstants.RESOURCE_LOADER, SpringResourceLoader.NAME);
|
||||
velocityEngine.setProperty(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER_CLASS, SpringResourceLoader.class.getName());
|
||||
velocityEngine.setProperty(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true");
|
||||
velocityEngine.setApplicationAttribute(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER, getResourceLoader());
|
||||
velocityEngine.setApplicationAttribute(
|
||||
SpringResourceLoader.SPRING_RESOURCE_LOADER_PATH, resourceLoaderPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be implemented by subclasses that want to to perform custom
|
||||
* post-processing of the VelocityEngine after this FactoryBean
|
||||
* performed its default configuration (but before VelocityEngine.init).
|
||||
* <p>Called by <code>createVelocityEngine()</code>.
|
||||
* @param velocityEngine the current VelocityEngine
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws VelocityException on Velocity initialization failure
|
||||
* @see #createVelocityEngine()
|
||||
* @see org.apache.velocity.app.VelocityEngine#init
|
||||
*/
|
||||
protected void postProcessVelocityEngine(VelocityEngine velocityEngine)
|
||||
throws IOException, VelocityException {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.velocity;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.exception.VelocityException;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
||||
/**
|
||||
* Factory bean that configures a VelocityEngine and provides it as bean
|
||||
* reference. This bean is intended for any kind of usage of Velocity in
|
||||
* application code, e.g. for generating email content. For web views,
|
||||
* VelocityConfigurer is used to set up a VelocityEngine for views.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a "resourceLoaderPath";
|
||||
* you do not need any further configuration then. For example, in a web
|
||||
* application context:
|
||||
*
|
||||
* <pre class="code"> <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
|
||||
* <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/>
|
||||
* </bean></pre>
|
||||
*
|
||||
* See the base class VelocityEngineFactory for configuration details.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @see #setConfigLocation
|
||||
* @see #setVelocityProperties
|
||||
* @see #setResourceLoaderPath
|
||||
* @see org.springframework.web.servlet.view.velocity.VelocityConfigurer
|
||||
*/
|
||||
public class VelocityEngineFactoryBean extends VelocityEngineFactory
|
||||
implements FactoryBean<VelocityEngine>, InitializingBean, ResourceLoaderAware {
|
||||
|
||||
private VelocityEngine velocityEngine;
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws IOException, VelocityException {
|
||||
this.velocityEngine = createVelocityEngine();
|
||||
}
|
||||
|
||||
|
||||
public VelocityEngine getObject() {
|
||||
return this.velocityEngine;
|
||||
}
|
||||
|
||||
public Class<? extends VelocityEngine> getObjectType() {
|
||||
return VelocityEngine.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.velocity;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.exception.VelocityException;
|
||||
|
||||
/**
|
||||
* Utility class for working with a VelocityEngine.
|
||||
* Provides convenience methods to merge a Velocity template with a model.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 22.01.2004
|
||||
*/
|
||||
public abstract class VelocityEngineUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(VelocityEngineUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model and write
|
||||
* the result to the given Writer.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @param writer the Writer to write the result to
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
*/
|
||||
public static void mergeTemplate(
|
||||
VelocityEngine velocityEngine, String templateLocation, Map model, Writer writer)
|
||||
throws VelocityException {
|
||||
|
||||
try {
|
||||
VelocityContext velocityContext = new VelocityContext(model);
|
||||
velocityEngine.mergeTemplate(templateLocation, velocityContext, writer);
|
||||
}
|
||||
catch (VelocityException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
|
||||
throw new VelocityException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model and write
|
||||
* the result to the given Writer.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param encoding the encoding of the template file
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @param writer the Writer to write the result to
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
*/
|
||||
public static void mergeTemplate(
|
||||
VelocityEngine velocityEngine, String templateLocation, String encoding, Map model, Writer writer)
|
||||
throws VelocityException {
|
||||
|
||||
try {
|
||||
VelocityContext velocityContext = new VelocityContext(model);
|
||||
velocityEngine.mergeTemplate(templateLocation, encoding, velocityContext, writer);
|
||||
}
|
||||
catch (VelocityException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Why does VelocityEngine throw a generic checked exception, after all?", ex);
|
||||
throw new VelocityException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model into a String.
|
||||
* <p>When using this method to prepare a text for a mail to be sent with Spring's
|
||||
* mail support, consider wrapping VelocityException in MailPreparationException.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @return the result as String
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
* @see org.springframework.mail.MailPreparationException
|
||||
*/
|
||||
public static String mergeTemplateIntoString(
|
||||
VelocityEngine velocityEngine, String templateLocation, Map model)
|
||||
throws VelocityException {
|
||||
|
||||
StringWriter result = new StringWriter();
|
||||
mergeTemplate(velocityEngine, templateLocation, model, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the specified Velocity template with the given model into a String.
|
||||
* <p>When using this method to prepare a text for a mail to be sent with Spring's
|
||||
* mail support, consider wrapping VelocityException in MailPreparationException.
|
||||
* @param velocityEngine VelocityEngine to work with
|
||||
* @param templateLocation the location of template, relative to Velocity's
|
||||
* resource loader path
|
||||
* @param encoding the encoding of the template file
|
||||
* @param model the Map that contains model names as keys and model objects
|
||||
* as values
|
||||
* @return the result as String
|
||||
* @throws VelocityException if the template wasn't found or rendering failed
|
||||
* @see org.springframework.mail.MailPreparationException
|
||||
*/
|
||||
public static String mergeTemplateIntoString(
|
||||
VelocityEngine velocityEngine, String templateLocation, String encoding, Map model)
|
||||
throws VelocityException {
|
||||
|
||||
StringWriter result = new StringWriter();
|
||||
mergeTemplate(velocityEngine, templateLocation, encoding, model, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for setting up
|
||||
* <a href="http://velocity.apache.org">Velocity</a>
|
||||
* within a Spring application context.
|
||||
*
|
||||
*/
|
||||
package org.springframework.ui.velocity;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
Support classes for integrating common third-party libraries into a Spring application context.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans;
|
||||
|
||||
import org.springframework.core.enums.ShortCodedLabeledEnum;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
*/
|
||||
public class Colour extends ShortCodedLabeledEnum {
|
||||
|
||||
public static final Colour RED = new Colour(0, "RED");
|
||||
public static final Colour BLUE = new Colour(1, "BLUE");
|
||||
public static final Colour GREEN = new Colour(2, "GREEN");
|
||||
public static final Colour PURPLE = new Colour(3, "PURPLE");
|
||||
|
||||
private Colour(int code, String label) {
|
||||
super(code, label);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans;
|
||||
|
||||
public interface INestedTestBean {
|
||||
|
||||
public String getCompany();
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans;
|
||||
|
||||
public interface IOther {
|
||||
|
||||
void absquatulate();
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface used for {@link org.springframework.beans.TestBean}.
|
||||
*
|
||||
* <p>Two methods are the same as on Person, but if this
|
||||
* extends person it breaks quite a few tests..
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public interface ITestBean {
|
||||
|
||||
int getAge();
|
||||
|
||||
void setAge(int age);
|
||||
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
|
||||
ITestBean getSpouse();
|
||||
|
||||
void setSpouse(ITestBean spouse);
|
||||
|
||||
ITestBean[] getSpouses();
|
||||
|
||||
String[] getStringArray();
|
||||
|
||||
void setStringArray(String[] stringArray);
|
||||
|
||||
/**
|
||||
* Throws a given (non-null) exception.
|
||||
*/
|
||||
void exceptional(Throwable t) throws Throwable;
|
||||
|
||||
Object returnsThis();
|
||||
|
||||
INestedTestBean getDoctor();
|
||||
|
||||
INestedTestBean getLawyer();
|
||||
|
||||
IndexedTestBean getNestedIndexedBean();
|
||||
|
||||
/**
|
||||
* Increment the age by one.
|
||||
* @return the previous age
|
||||
*/
|
||||
int haveBirthday();
|
||||
|
||||
void unreliableFileOperation() throws IOException;
|
||||
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 11.11.2003
|
||||
*/
|
||||
public class IndexedTestBean {
|
||||
|
||||
private TestBean[] array;
|
||||
|
||||
private Collection collection;
|
||||
|
||||
private List list;
|
||||
|
||||
private Set set;
|
||||
|
||||
private SortedSet sortedSet;
|
||||
|
||||
private Map map;
|
||||
|
||||
private SortedMap sortedMap;
|
||||
|
||||
|
||||
public IndexedTestBean() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public IndexedTestBean(boolean populate) {
|
||||
if (populate) {
|
||||
populate();
|
||||
}
|
||||
}
|
||||
|
||||
public void populate() {
|
||||
TestBean tb0 = new TestBean("name0", 0);
|
||||
TestBean tb1 = new TestBean("name1", 0);
|
||||
TestBean tb2 = new TestBean("name2", 0);
|
||||
TestBean tb3 = new TestBean("name3", 0);
|
||||
TestBean tb4 = new TestBean("name4", 0);
|
||||
TestBean tb5 = new TestBean("name5", 0);
|
||||
TestBean tb6 = new TestBean("name6", 0);
|
||||
TestBean tb7 = new TestBean("name7", 0);
|
||||
TestBean tbX = new TestBean("nameX", 0);
|
||||
TestBean tbY = new TestBean("nameY", 0);
|
||||
this.array = new TestBean[] {tb0, tb1};
|
||||
this.list = new ArrayList();
|
||||
this.list.add(tb2);
|
||||
this.list.add(tb3);
|
||||
this.set = new TreeSet();
|
||||
this.set.add(tb6);
|
||||
this.set.add(tb7);
|
||||
this.map = new HashMap();
|
||||
this.map.put("key1", tb4);
|
||||
this.map.put("key2", tb5);
|
||||
this.map.put("key.3", tb5);
|
||||
List list = new ArrayList();
|
||||
list.add(tbX);
|
||||
list.add(tbY);
|
||||
this.map.put("key4", list);
|
||||
}
|
||||
|
||||
|
||||
public TestBean[] getArray() {
|
||||
return array;
|
||||
}
|
||||
|
||||
public void setArray(TestBean[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public Collection getCollection() {
|
||||
return collection;
|
||||
}
|
||||
|
||||
public void setCollection(Collection collection) {
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
public List getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public Set getSet() {
|
||||
return set;
|
||||
}
|
||||
|
||||
public void setSet(Set set) {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
public SortedSet getSortedSet() {
|
||||
return sortedSet;
|
||||
}
|
||||
|
||||
public void setSortedSet(SortedSet sortedSet) {
|
||||
this.sortedSet = sortedSet;
|
||||
}
|
||||
|
||||
public Map getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public void setMap(Map map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public SortedMap getSortedMap() {
|
||||
return sortedMap;
|
||||
}
|
||||
|
||||
public void setSortedMap(SortedMap sortedMap) {
|
||||
this.sortedMap = sortedMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans;
|
||||
|
||||
/**
|
||||
* Simple nested test bean used for testing bean factories, AOP framework etc.
|
||||
*
|
||||
* @author Trevor D. Cook
|
||||
* @since 30.09.2003
|
||||
*/
|
||||
public class NestedTestBean implements INestedTestBean {
|
||||
|
||||
private String company = "";
|
||||
|
||||
public NestedTestBean() {
|
||||
}
|
||||
|
||||
public NestedTestBean(String company) {
|
||||
setCompany(company);
|
||||
}
|
||||
|
||||
public void setCompany(String company) {
|
||||
this.company = (company != null ? company : "");
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof NestedTestBean)) {
|
||||
return false;
|
||||
}
|
||||
NestedTestBean ntb = (NestedTestBean) obj;
|
||||
return this.company.equals(ntb.company);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.company.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "NestedTestBean: " + this.company;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
*
|
||||
* 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.springframework.beans;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Simple test bean used for testing bean factories, the AOP framework etc.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 15 April 2001
|
||||
*/
|
||||
public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOther, Comparable {
|
||||
|
||||
private String beanName;
|
||||
|
||||
private String country;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private boolean postProcessed;
|
||||
|
||||
private String name;
|
||||
|
||||
private String sex;
|
||||
|
||||
private int age;
|
||||
|
||||
private boolean jedi;
|
||||
|
||||
private ITestBean[] spouses;
|
||||
|
||||
private String touchy;
|
||||
|
||||
private String[] stringArray;
|
||||
|
||||
private Integer[] someIntegerArray;
|
||||
|
||||
private Date date = new Date();
|
||||
|
||||
private Float myFloat = new Float(0.0);
|
||||
|
||||
private Collection friends = new LinkedList();
|
||||
|
||||
private Set someSet = new HashSet();
|
||||
|
||||
private Map someMap = new HashMap();
|
||||
|
||||
private List someList = new ArrayList();
|
||||
|
||||
private Properties someProperties = new Properties();
|
||||
|
||||
private INestedTestBean doctor = new NestedTestBean();
|
||||
|
||||
private INestedTestBean lawyer = new NestedTestBean();
|
||||
|
||||
private IndexedTestBean nestedIndexedBean;
|
||||
|
||||
private boolean destroyed;
|
||||
|
||||
private Number someNumber;
|
||||
|
||||
private Colour favouriteColour;
|
||||
|
||||
private Boolean someBoolean;
|
||||
|
||||
private List otherColours;
|
||||
|
||||
private List pets;
|
||||
|
||||
|
||||
public TestBean() {
|
||||
}
|
||||
|
||||
public TestBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public TestBean(ITestBean spouse) {
|
||||
this.spouses = new ITestBean[] {spouse};
|
||||
}
|
||||
|
||||
public TestBean(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public TestBean(ITestBean spouse, Properties someProperties) {
|
||||
this.spouses = new ITestBean[] {spouse};
|
||||
this.someProperties = someProperties;
|
||||
}
|
||||
|
||||
public TestBean(List someList) {
|
||||
this.someList = someList;
|
||||
}
|
||||
|
||||
public TestBean(Set someSet) {
|
||||
this.someSet = someSet;
|
||||
}
|
||||
|
||||
public TestBean(Map someMap) {
|
||||
this.someMap = someMap;
|
||||
}
|
||||
|
||||
public TestBean(Properties someProperties) {
|
||||
this.someProperties = someProperties;
|
||||
}
|
||||
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public String getBeanName() {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
public void setPostProcessed(boolean postProcessed) {
|
||||
this.postProcessed = postProcessed;
|
||||
}
|
||||
|
||||
public boolean isPostProcessed() {
|
||||
return postProcessed;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(String sex) {
|
||||
this.sex = sex;
|
||||
if (this.name == null) {
|
||||
this.name = sex;
|
||||
}
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public boolean isJedi() {
|
||||
return jedi;
|
||||
}
|
||||
|
||||
public void setJedi(boolean jedi) {
|
||||
this.jedi = jedi;
|
||||
}
|
||||
|
||||
public ITestBean getSpouse() {
|
||||
return (spouses != null ? spouses[0] : null);
|
||||
}
|
||||
|
||||
public void setSpouse(ITestBean spouse) {
|
||||
this.spouses = new ITestBean[] {spouse};
|
||||
}
|
||||
|
||||
public ITestBean[] getSpouses() {
|
||||
return spouses;
|
||||
}
|
||||
|
||||
public String getTouchy() {
|
||||
return touchy;
|
||||
}
|
||||
|
||||
public void setTouchy(String touchy) throws Exception {
|
||||
if (touchy.indexOf('.') != -1) {
|
||||
throw new Exception("Can't contain a .");
|
||||
}
|
||||
if (touchy.indexOf(',') != -1) {
|
||||
throw new NumberFormatException("Number format exception: contains a ,");
|
||||
}
|
||||
this.touchy = touchy;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String[] getStringArray() {
|
||||
return stringArray;
|
||||
}
|
||||
|
||||
public void setStringArray(String[] stringArray) {
|
||||
this.stringArray = stringArray;
|
||||
}
|
||||
|
||||
public Integer[] getSomeIntegerArray() {
|
||||
return someIntegerArray;
|
||||
}
|
||||
|
||||
public void setSomeIntegerArray(Integer[] someIntegerArray) {
|
||||
this.someIntegerArray = someIntegerArray;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public Float getMyFloat() {
|
||||
return myFloat;
|
||||
}
|
||||
|
||||
public void setMyFloat(Float myFloat) {
|
||||
this.myFloat = myFloat;
|
||||
}
|
||||
|
||||
public Collection getFriends() {
|
||||
return friends;
|
||||
}
|
||||
|
||||
public void setFriends(Collection friends) {
|
||||
this.friends = friends;
|
||||
}
|
||||
|
||||
public Set getSomeSet() {
|
||||
return someSet;
|
||||
}
|
||||
|
||||
public void setSomeSet(Set someSet) {
|
||||
this.someSet = someSet;
|
||||
}
|
||||
|
||||
public Map getSomeMap() {
|
||||
return someMap;
|
||||
}
|
||||
|
||||
public void setSomeMap(Map someMap) {
|
||||
this.someMap = someMap;
|
||||
}
|
||||
|
||||
public List getSomeList() {
|
||||
return someList;
|
||||
}
|
||||
|
||||
public void setSomeList(List someList) {
|
||||
this.someList = someList;
|
||||
}
|
||||
|
||||
public Properties getSomeProperties() {
|
||||
return someProperties;
|
||||
}
|
||||
|
||||
public void setSomeProperties(Properties someProperties) {
|
||||
this.someProperties = someProperties;
|
||||
}
|
||||
|
||||
public INestedTestBean getDoctor() {
|
||||
return doctor;
|
||||
}
|
||||
|
||||
public void setDoctor(INestedTestBean doctor) {
|
||||
this.doctor = doctor;
|
||||
}
|
||||
|
||||
public INestedTestBean getLawyer() {
|
||||
return lawyer;
|
||||
}
|
||||
|
||||
public void setLawyer(INestedTestBean lawyer) {
|
||||
this.lawyer = lawyer;
|
||||
}
|
||||
|
||||
public Number getSomeNumber() {
|
||||
return someNumber;
|
||||
}
|
||||
|
||||
public void setSomeNumber(Number someNumber) {
|
||||
this.someNumber = someNumber;
|
||||
}
|
||||
|
||||
public Colour getFavouriteColour() {
|
||||
return favouriteColour;
|
||||
}
|
||||
|
||||
public void setFavouriteColour(Colour favouriteColour) {
|
||||
this.favouriteColour = favouriteColour;
|
||||
}
|
||||
|
||||
public Boolean getSomeBoolean() {
|
||||
return someBoolean;
|
||||
}
|
||||
|
||||
public void setSomeBoolean(Boolean someBoolean) {
|
||||
this.someBoolean = someBoolean;
|
||||
}
|
||||
|
||||
public IndexedTestBean getNestedIndexedBean() {
|
||||
return nestedIndexedBean;
|
||||
}
|
||||
|
||||
public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) {
|
||||
this.nestedIndexedBean = nestedIndexedBean;
|
||||
}
|
||||
|
||||
public List getOtherColours() {
|
||||
return otherColours;
|
||||
}
|
||||
|
||||
public void setOtherColours(List otherColours) {
|
||||
this.otherColours = otherColours;
|
||||
}
|
||||
|
||||
public List getPets() {
|
||||
return pets;
|
||||
}
|
||||
|
||||
public void setPets(List pets) {
|
||||
this.pets = pets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see org.springframework.beans.ITestBean#exceptional(Throwable)
|
||||
*/
|
||||
public void exceptional(Throwable t) throws Throwable {
|
||||
if (t != null) {
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
|
||||
public void unreliableFileOperation() throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
/**
|
||||
* @see org.springframework.beans.ITestBean#returnsThis()
|
||||
*/
|
||||
public Object returnsThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.springframework.beans.IOther#absquatulate()
|
||||
*/
|
||||
public void absquatulate() {
|
||||
}
|
||||
|
||||
public int haveBirthday() {
|
||||
return age++;
|
||||
}
|
||||
|
||||
|
||||
public void destroy() {
|
||||
this.destroyed = true;
|
||||
}
|
||||
|
||||
public boolean wasDestroyed() {
|
||||
return destroyed;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || !(other instanceof TestBean)) {
|
||||
return false;
|
||||
}
|
||||
TestBean tb2 = (TestBean) other;
|
||||
return (ObjectUtils.nullSafeEquals(this.name, tb2.name) && this.age == tb2.age);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.age;
|
||||
}
|
||||
|
||||
public int compareTo(Object other) {
|
||||
if (this.name != null && other instanceof TestBean) {
|
||||
return this.name.compareTo(((TestBean) other).getName());
|
||||
}
|
||||
else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @author Rick Evans
|
||||
* @author Chris Beams
|
||||
* @since 10.09.2003
|
||||
*/
|
||||
public final class SimpleMailMessageTests {
|
||||
|
||||
@Test
|
||||
public void testSimpleMessageCopyCtor() {
|
||||
SimpleMailMessage message = new SimpleMailMessage();
|
||||
message.setFrom("me@mail.org");
|
||||
message.setTo("you@mail.org");
|
||||
|
||||
SimpleMailMessage messageCopy = new SimpleMailMessage(message);
|
||||
assertEquals("me@mail.org", messageCopy.getFrom());
|
||||
assertEquals("you@mail.org", messageCopy.getTo()[0]);
|
||||
|
||||
message.setReplyTo("reply@mail.org");
|
||||
message.setCc(new String[]{"he@mail.org", "she@mail.org"});
|
||||
message.setBcc(new String[]{"us@mail.org", "them@mail.org"});
|
||||
Date sentDate = new Date();
|
||||
message.setSentDate(sentDate);
|
||||
message.setSubject("my subject");
|
||||
message.setText("my text");
|
||||
|
||||
assertEquals("me@mail.org", message.getFrom());
|
||||
assertEquals("reply@mail.org", message.getReplyTo());
|
||||
assertEquals("you@mail.org", message.getTo()[0]);
|
||||
List<String> ccs = Arrays.asList(message.getCc());
|
||||
assertTrue(ccs.contains("he@mail.org"));
|
||||
assertTrue(ccs.contains("she@mail.org"));
|
||||
List<String> bccs = Arrays.asList(message.getBcc());
|
||||
assertTrue(bccs.contains("us@mail.org"));
|
||||
assertTrue(bccs.contains("them@mail.org"));
|
||||
assertEquals(sentDate, message.getSentDate());
|
||||
assertEquals("my subject", message.getSubject());
|
||||
assertEquals("my text", message.getText());
|
||||
|
||||
messageCopy = new SimpleMailMessage(message);
|
||||
assertEquals("me@mail.org", messageCopy.getFrom());
|
||||
assertEquals("reply@mail.org", messageCopy.getReplyTo());
|
||||
assertEquals("you@mail.org", messageCopy.getTo()[0]);
|
||||
ccs = Arrays.asList(messageCopy.getCc());
|
||||
assertTrue(ccs.contains("he@mail.org"));
|
||||
assertTrue(ccs.contains("she@mail.org"));
|
||||
bccs = Arrays.asList(message.getBcc());
|
||||
assertTrue(bccs.contains("us@mail.org"));
|
||||
assertTrue(bccs.contains("them@mail.org"));
|
||||
assertEquals(sentDate, messageCopy.getSentDate());
|
||||
assertEquals("my subject", messageCopy.getSubject());
|
||||
assertEquals("my text", messageCopy.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeepCopyOfStringArrayTypedFieldsOnCopyCtor() throws Exception {
|
||||
|
||||
SimpleMailMessage original = new SimpleMailMessage();
|
||||
original.setTo(new String[]{"fiona@mail.org", "apple@mail.org"});
|
||||
original.setCc(new String[]{"he@mail.org", "she@mail.org"});
|
||||
original.setBcc(new String[]{"us@mail.org", "them@mail.org"});
|
||||
|
||||
SimpleMailMessage copy = new SimpleMailMessage(original);
|
||||
|
||||
original.getTo()[0] = "mmm@mmm.org";
|
||||
original.getCc()[0] = "mmm@mmm.org";
|
||||
original.getBcc()[0] = "mmm@mmm.org";
|
||||
|
||||
assertEquals("fiona@mail.org", copy.getTo()[0]);
|
||||
assertEquals("he@mail.org", copy.getCc()[0]);
|
||||
assertEquals("us@mail.org", copy.getBcc()[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that two equal SimpleMailMessages have equal hash codes.
|
||||
*/
|
||||
@Test
|
||||
public final void testHashCode() {
|
||||
SimpleMailMessage message1 = new SimpleMailMessage();
|
||||
message1.setFrom("from@somewhere");
|
||||
message1.setReplyTo("replyTo@somewhere");
|
||||
message1.setTo("to@somewhere");
|
||||
message1.setCc("cc@somewhere");
|
||||
message1.setBcc("bcc@somewhere");
|
||||
message1.setSentDate(new Date());
|
||||
message1.setSubject("subject");
|
||||
message1.setText("text");
|
||||
|
||||
// Copy the message
|
||||
SimpleMailMessage message2 = new SimpleMailMessage(message1);
|
||||
|
||||
assertEquals(message1, message2);
|
||||
assertEquals(message1.hashCode(), message2.hashCode());
|
||||
}
|
||||
|
||||
public final void testEqualsObject() {
|
||||
SimpleMailMessage message1;
|
||||
SimpleMailMessage message2;
|
||||
|
||||
// Same object is equal
|
||||
message1 = new SimpleMailMessage();
|
||||
message2 = message1;
|
||||
assertTrue(message1.equals(message2));
|
||||
|
||||
// Null object is not equal
|
||||
message1 = new SimpleMailMessage();
|
||||
message2 = null;
|
||||
assertTrue(!(message1.equals(message2)));
|
||||
|
||||
// Different class is not equal
|
||||
assertTrue(!(message1.equals(new Object())));
|
||||
|
||||
// Equal values are equal
|
||||
message1 = new SimpleMailMessage();
|
||||
message2 = new SimpleMailMessage();
|
||||
assertTrue(message1.equals(message2));
|
||||
|
||||
message1 = new SimpleMailMessage();
|
||||
message1.setFrom("from@somewhere");
|
||||
message1.setReplyTo("replyTo@somewhere");
|
||||
message1.setTo("to@somewhere");
|
||||
message1.setCc("cc@somewhere");
|
||||
message1.setBcc("bcc@somewhere");
|
||||
message1.setSentDate(new Date());
|
||||
message1.setSubject("subject");
|
||||
message1.setText("text");
|
||||
message2 = new SimpleMailMessage(message1);
|
||||
assertTrue(message1.equals(message2));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testCopyCtorChokesOnNullOriginalMessage() throws Exception {
|
||||
new SimpleMailMessage(null);
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testCopyToChokesOnNullTargetMessage() throws Exception {
|
||||
new SimpleMailMessage().copyTo(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class ConfigurableMimeFileTypeMapTests extends TestCase {
|
||||
|
||||
public void testAgainstDefaultConfiguration() throws Exception {
|
||||
ConfigurableMimeFileTypeMap ftm = new ConfigurableMimeFileTypeMap();
|
||||
ftm.afterPropertiesSet();
|
||||
|
||||
assertEquals("Invalid content type for HTM", "text/html", ftm.getContentType("foobar.HTM"));
|
||||
assertEquals("Invalid content type for html", "text/html", ftm.getContentType("foobar.html"));
|
||||
assertEquals("Invalid content type for c++", "text/plain", ftm.getContentType("foobar.c++"));
|
||||
assertEquals("Invalid content type for svf", "image/vnd.svf", ftm.getContentType("foobar.svf"));
|
||||
assertEquals("Invalid content type for dsf", "image/x-mgx-dsf", ftm.getContentType("foobar.dsf"));
|
||||
assertEquals("Invalid default content type", "application/octet-stream", ftm.getContentType("foobar.foo"));
|
||||
}
|
||||
|
||||
public void testAgainstDefaultConfigurationWithFilePath() throws Exception {
|
||||
ConfigurableMimeFileTypeMap ftm = new ConfigurableMimeFileTypeMap();
|
||||
assertEquals("Invalid content type for HTM", "text/html", ftm.getContentType(new File("/tmp/foobar.HTM")));
|
||||
}
|
||||
|
||||
public void testWithAdditionalMappings() throws Exception {
|
||||
ConfigurableMimeFileTypeMap ftm = new ConfigurableMimeFileTypeMap();
|
||||
ftm.setMappings(new String[] {"foo/bar HTM foo", "foo/cpp c++"});
|
||||
ftm.afterPropertiesSet();
|
||||
|
||||
assertEquals("Invalid content type for HTM - override didn't work", "foo/bar", ftm.getContentType("foobar.HTM"));
|
||||
assertEquals("Invalid content type for c++ - override didn't work", "foo/cpp", ftm.getContentType("foobar.c++"));
|
||||
assertEquals("Invalid content type for foo - new mapping didn't work", "foo/bar", ftm.getContentType("bar.foo"));
|
||||
}
|
||||
|
||||
public void testWithCustomMappingLocation() throws Exception {
|
||||
Resource resource = new ClassPathResource("test.mime.types", getClass());
|
||||
|
||||
ConfigurableMimeFileTypeMap ftm = new ConfigurableMimeFileTypeMap();
|
||||
ftm.setMappingLocation(resource);
|
||||
ftm.afterPropertiesSet();
|
||||
|
||||
assertEquals("Invalid content type for foo", "text/foo", ftm.getContentType("foobar.foo"));
|
||||
assertEquals("Invalid content type for bar", "text/bar", ftm.getContentType("foobar.bar"));
|
||||
assertEquals("Invalid content type for fimg", "image/foo", ftm.getContentType("foobar.fimg"));
|
||||
assertEquals("Invalid content type for bimg", "image/bar", ftm.getContentType("foobar.bimg"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author Brian Hanafee
|
||||
* @since 09.07.2005
|
||||
*/
|
||||
public class InternetAddressEditorTests extends TestCase {
|
||||
|
||||
private static final String EMPTY = "";
|
||||
private static final String SIMPLE = "nobody@nowhere.com";
|
||||
private static final String BAD = "(";
|
||||
|
||||
private InternetAddressEditor editor;
|
||||
|
||||
protected void setUp() {
|
||||
editor = new InternetAddressEditor();
|
||||
}
|
||||
|
||||
public void testUninitialized() {
|
||||
assertEquals("Uninitialized editor did not return empty value string", EMPTY, editor.getAsText());
|
||||
}
|
||||
|
||||
public void testSetNull() {
|
||||
editor.setAsText(null);
|
||||
assertEquals("Setting null did not result in empty value string", EMPTY, editor.getAsText());
|
||||
}
|
||||
|
||||
public void testSetEmpty() {
|
||||
editor.setAsText(EMPTY);
|
||||
assertEquals("Setting empty string did not result in empty value string", EMPTY, editor.getAsText());
|
||||
}
|
||||
|
||||
public void testAllWhitespace() {
|
||||
editor.setAsText(" ");
|
||||
assertEquals("All whitespace was not recognized", EMPTY, editor.getAsText());
|
||||
}
|
||||
|
||||
public void testSimpleGoodAddess() {
|
||||
editor.setAsText(SIMPLE);
|
||||
assertEquals("Simple email address failed", SIMPLE, editor.getAsText());
|
||||
}
|
||||
|
||||
public void testExcessWhitespace() {
|
||||
editor.setAsText(" " + SIMPLE + " ");
|
||||
assertEquals("Whitespace was not stripped", SIMPLE, editor.getAsText());
|
||||
}
|
||||
|
||||
public void testSimpleBadAddress() {
|
||||
try {
|
||||
editor.setAsText(BAD);
|
||||
fail("Should have failed on \"" + BAD + "\", instead got " + editor.getAsText());
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// Passed the test
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,564 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* 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.springframework.mail.javamail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.mail.Address;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.NoSuchProviderException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.URLName;
|
||||
import javax.mail.internet.AddressException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.mail.MailParseException;
|
||||
import org.springframework.mail.MailSendException;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 09.10.2004
|
||||
*/
|
||||
public class JavaMailSenderTests extends TestCase {
|
||||
|
||||
public void testJavaMailSenderWithSimpleMessage() throws MessagingException, IOException {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setPort(30);
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
SimpleMailMessage simpleMessage = new SimpleMailMessage();
|
||||
simpleMessage.setFrom("me@mail.org");
|
||||
simpleMessage.setReplyTo("reply@mail.org");
|
||||
simpleMessage.setTo("you@mail.org");
|
||||
simpleMessage.setCc(new String[] {"he@mail.org", "she@mail.org"});
|
||||
simpleMessage.setBcc(new String[] {"us@mail.org", "them@mail.org"});
|
||||
Date sentDate = new Date(2004, 1, 1);
|
||||
simpleMessage.setSentDate(sentDate);
|
||||
simpleMessage.setSubject("my subject");
|
||||
simpleMessage.setText("my text");
|
||||
sender.send(simpleMessage);
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals(30, sender.transport.getConnectedPort());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
MimeMessage sentMessage = sender.transport.getSentMessage(0);
|
||||
List froms = Arrays.asList(sentMessage.getFrom());
|
||||
assertEquals(1, froms.size());
|
||||
assertEquals("me@mail.org", ((InternetAddress) froms.get(0)).getAddress());
|
||||
List replyTos = Arrays.asList(sentMessage.getReplyTo());
|
||||
assertEquals("reply@mail.org", ((InternetAddress) replyTos.get(0)).getAddress());
|
||||
List tos = Arrays.asList(sentMessage.getRecipients(Message.RecipientType.TO));
|
||||
assertEquals(1, tos.size());
|
||||
assertEquals("you@mail.org", ((InternetAddress) tos.get(0)).getAddress());
|
||||
List ccs = Arrays.asList(sentMessage.getRecipients(Message.RecipientType.CC));
|
||||
assertEquals(2, ccs.size());
|
||||
assertEquals("he@mail.org", ((InternetAddress) ccs.get(0)).getAddress());
|
||||
assertEquals("she@mail.org", ((InternetAddress) ccs.get(1)).getAddress());
|
||||
List bccs = Arrays.asList(sentMessage.getRecipients(Message.RecipientType.BCC));
|
||||
assertEquals(2, bccs.size());
|
||||
assertEquals("us@mail.org", ((InternetAddress) bccs.get(0)).getAddress());
|
||||
assertEquals("them@mail.org", ((InternetAddress) bccs.get(1)).getAddress());
|
||||
assertEquals(sentDate.getTime(), sentMessage.getSentDate().getTime());
|
||||
assertEquals("my subject", sentMessage.getSubject());
|
||||
assertEquals("my text", sentMessage.getContent());
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithSimpleMessages() throws MessagingException, IOException {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
SimpleMailMessage simpleMessage1 = new SimpleMailMessage();
|
||||
simpleMessage1.setTo("he@mail.org");
|
||||
SimpleMailMessage simpleMessage2 = new SimpleMailMessage();
|
||||
simpleMessage2.setTo("she@mail.org");
|
||||
sender.send(new SimpleMailMessage[] {simpleMessage1, simpleMessage2});
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
|
||||
assertEquals(2, sender.transport.getSentMessages().size());
|
||||
MimeMessage sentMessage1 = sender.transport.getSentMessage(0);
|
||||
List tos1 = Arrays.asList(sentMessage1.getRecipients(Message.RecipientType.TO));
|
||||
assertEquals(1, tos1.size());
|
||||
assertEquals("he@mail.org", ((InternetAddress) tos1.get(0)).getAddress());
|
||||
MimeMessage sentMessage2 = sender.transport.getSentMessage(1);
|
||||
List tos2 = Arrays.asList(sentMessage2.getRecipients(Message.RecipientType.TO));
|
||||
assertEquals(1, tos2.size());
|
||||
assertEquals("she@mail.org", ((InternetAddress) tos2.get(0)).getAddress());
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithMimeMessage() throws MessagingException {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
MimeMessage mimeMessage = sender.createMimeMessage();
|
||||
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
|
||||
sender.send(mimeMessage);
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(mimeMessage, sender.transport.getSentMessage(0));
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithMimeMessages() throws MessagingException {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
MimeMessage mimeMessage1 = sender.createMimeMessage();
|
||||
mimeMessage1.setRecipient(Message.RecipientType.TO, new InternetAddress("he@mail.org"));
|
||||
MimeMessage mimeMessage2 = sender.createMimeMessage();
|
||||
mimeMessage2.setRecipient(Message.RecipientType.TO, new InternetAddress("she@mail.org"));
|
||||
sender.send(new MimeMessage[] {mimeMessage1, mimeMessage2});
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(2, sender.transport.getSentMessages().size());
|
||||
assertEquals(mimeMessage1, sender.transport.getSentMessage(0));
|
||||
assertEquals(mimeMessage2, sender.transport.getSentMessage(1));
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithMimeMessagePreparator() {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
final List<Message> messages = new ArrayList<Message>();
|
||||
|
||||
MimeMessagePreparator preparator = new MimeMessagePreparator() {
|
||||
public void prepare(MimeMessage mimeMessage) throws MessagingException {
|
||||
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
|
||||
messages.add(mimeMessage);
|
||||
}
|
||||
};
|
||||
sender.send(preparator);
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(messages.get(0), sender.transport.getSentMessage(0));
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithMimeMessagePreparators() {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
final List<Message> messages = new ArrayList<Message>();
|
||||
|
||||
MimeMessagePreparator preparator1 = new MimeMessagePreparator() {
|
||||
public void prepare(MimeMessage mimeMessage) throws MessagingException {
|
||||
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("he@mail.org"));
|
||||
messages.add(mimeMessage);
|
||||
}
|
||||
};
|
||||
MimeMessagePreparator preparator2 = new MimeMessagePreparator() {
|
||||
public void prepare(MimeMessage mimeMessage) throws MessagingException {
|
||||
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("she@mail.org"));
|
||||
messages.add(mimeMessage);
|
||||
}
|
||||
};
|
||||
sender.send(new MimeMessagePreparator[] {preparator1, preparator2});
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(2, sender.transport.getSentMessages().size());
|
||||
assertEquals(messages.get(0), sender.transport.getSentMessage(0));
|
||||
assertEquals(messages.get(1), sender.transport.getSentMessage(1));
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithMimeMessageHelper() throws MessagingException {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
MimeMessageHelper message = new MimeMessageHelper(sender.createMimeMessage());
|
||||
assertNull(message.getEncoding());
|
||||
assertTrue(message.getFileTypeMap() instanceof ConfigurableMimeFileTypeMap);
|
||||
|
||||
message.setTo("you@mail.org");
|
||||
sender.send(message.getMimeMessage());
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(message.getMimeMessage(), sender.transport.getSentMessage(0));
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithMimeMessageHelperAndSpecificEncoding() throws MessagingException {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
MimeMessageHelper message = new MimeMessageHelper(sender.createMimeMessage(), "UTF-8");
|
||||
assertEquals("UTF-8", message.getEncoding());
|
||||
FileTypeMap fileTypeMap = new ConfigurableMimeFileTypeMap();
|
||||
message.setFileTypeMap(fileTypeMap);
|
||||
assertEquals(fileTypeMap, message.getFileTypeMap());
|
||||
|
||||
message.setTo("you@mail.org");
|
||||
sender.send(message.getMimeMessage());
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(message.getMimeMessage(), sender.transport.getSentMessage(0));
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithMimeMessageHelperAndDefaultEncoding() throws MessagingException {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
sender.setDefaultEncoding("UTF-8");
|
||||
|
||||
FileTypeMap fileTypeMap = new ConfigurableMimeFileTypeMap();
|
||||
sender.setDefaultFileTypeMap(fileTypeMap);
|
||||
MimeMessageHelper message = new MimeMessageHelper(sender.createMimeMessage());
|
||||
assertEquals("UTF-8", message.getEncoding());
|
||||
assertEquals(fileTypeMap, message.getFileTypeMap());
|
||||
|
||||
message.setTo("you@mail.org");
|
||||
sender.send(message.getMimeMessage());
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(message.getMimeMessage(), sender.transport.getSentMessage(0));
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithParseExceptionOnSimpleMessage() {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
SimpleMailMessage simpleMessage = new SimpleMailMessage();
|
||||
simpleMessage.setFrom("");
|
||||
try {
|
||||
sender.send(simpleMessage);
|
||||
}
|
||||
catch (MailParseException ex) {
|
||||
// expected
|
||||
assertTrue(ex.getCause() instanceof AddressException);
|
||||
}
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithParseExceptionOnMimeMessagePreparator() {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
MimeMessagePreparator preparator = new MimeMessagePreparator() {
|
||||
public void prepare(MimeMessage mimeMessage) throws MessagingException {
|
||||
mimeMessage.setFrom(new InternetAddress(""));
|
||||
}
|
||||
};
|
||||
try {
|
||||
sender.send(preparator);
|
||||
}
|
||||
catch (MailParseException ex) {
|
||||
// expected
|
||||
assertTrue(ex.getCause() instanceof AddressException);
|
||||
}
|
||||
}
|
||||
|
||||
public void testJavaMailSenderWithCustomSession() throws MessagingException {
|
||||
final Session session = Session.getInstance(new Properties());
|
||||
MockJavaMailSender sender = new MockJavaMailSender() {
|
||||
@Override
|
||||
protected Transport getTransport(Session sess) throws NoSuchProviderException {
|
||||
assertEquals(session, sess);
|
||||
return super.getTransport(sess);
|
||||
}
|
||||
};
|
||||
sender.setSession(session);
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
MimeMessage mimeMessage = sender.createMimeMessage();
|
||||
mimeMessage.setSubject("custom");
|
||||
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
|
||||
mimeMessage.setSentDate(new Date(2005, 3, 1));
|
||||
sender.send(mimeMessage);
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(mimeMessage, sender.transport.getSentMessage(0));
|
||||
}
|
||||
|
||||
public void testJavaMailProperties() throws MessagingException {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("bogusKey", "bogusValue");
|
||||
MockJavaMailSender sender = new MockJavaMailSender() {
|
||||
@Override
|
||||
protected Transport getTransport(Session sess) throws NoSuchProviderException {
|
||||
assertEquals("bogusValue", sess.getProperty("bogusKey"));
|
||||
return super.getTransport(sess);
|
||||
}
|
||||
};
|
||||
sender.setJavaMailProperties(props);
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
MimeMessage mimeMessage = sender.createMimeMessage();
|
||||
mimeMessage.setRecipient(Message.RecipientType.TO, new InternetAddress("you@mail.org"));
|
||||
sender.send(mimeMessage);
|
||||
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(mimeMessage, sender.transport.getSentMessage(0));
|
||||
}
|
||||
|
||||
public void testFailedMailServerConnect() throws Exception {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost(null);
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
SimpleMailMessage simpleMessage1 = new SimpleMailMessage();
|
||||
try {
|
||||
sender.send(simpleMessage1);
|
||||
fail("Should have thrown MailSendException");
|
||||
}
|
||||
catch (MailSendException ex) {
|
||||
// expected
|
||||
ex.printStackTrace();
|
||||
assertTrue(ex.getFailedMessages() != null);
|
||||
assertEquals(1, ex.getFailedMessages().size());
|
||||
assertSame(simpleMessage1, ex.getFailedMessages().keySet().iterator().next());
|
||||
assertSame(ex.getCause(), ex.getFailedMessages().values().iterator().next());
|
||||
}
|
||||
}
|
||||
|
||||
public void testFailedMailServerClose() throws Exception {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
SimpleMailMessage simpleMessage1 = new SimpleMailMessage();
|
||||
try {
|
||||
sender.send(simpleMessage1);
|
||||
fail("Should have thrown MailSendException");
|
||||
}
|
||||
catch (MailSendException ex) {
|
||||
// expected
|
||||
ex.printStackTrace();
|
||||
assertTrue(ex.getFailedMessages() != null);
|
||||
assertEquals(0, ex.getFailedMessages().size());
|
||||
}
|
||||
}
|
||||
|
||||
public void testFailedSimpleMessage() throws Exception {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
SimpleMailMessage simpleMessage1 = new SimpleMailMessage();
|
||||
simpleMessage1.setTo("he@mail.org");
|
||||
simpleMessage1.setSubject("fail");
|
||||
SimpleMailMessage simpleMessage2 = new SimpleMailMessage();
|
||||
simpleMessage2.setTo("she@mail.org");
|
||||
|
||||
try {
|
||||
sender.send(new SimpleMailMessage[] {simpleMessage1, simpleMessage2});
|
||||
}
|
||||
catch (MailSendException ex) {
|
||||
ex.printStackTrace();
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(new InternetAddress("she@mail.org"), sender.transport.getSentMessage(0).getAllRecipients()[0]);
|
||||
assertEquals(1, ex.getFailedMessages().size());
|
||||
assertEquals(simpleMessage1, ex.getFailedMessages().keySet().iterator().next());
|
||||
Object subEx = ex.getFailedMessages().values().iterator().next();
|
||||
assertTrue(subEx instanceof MessagingException);
|
||||
assertEquals("failed", ((MessagingException) subEx).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testFailedMimeMessage() throws Exception {
|
||||
MockJavaMailSender sender = new MockJavaMailSender();
|
||||
sender.setHost("host");
|
||||
sender.setUsername("username");
|
||||
sender.setPassword("password");
|
||||
|
||||
MimeMessage mimeMessage1 = sender.createMimeMessage();
|
||||
mimeMessage1.setRecipient(Message.RecipientType.TO, new InternetAddress("he@mail.org"));
|
||||
mimeMessage1.setSubject("fail");
|
||||
MimeMessage mimeMessage2 = sender.createMimeMessage();
|
||||
mimeMessage2.setRecipient(Message.RecipientType.TO, new InternetAddress("she@mail.org"));
|
||||
|
||||
try {
|
||||
sender.send(new MimeMessage[] {mimeMessage1, mimeMessage2});
|
||||
}
|
||||
catch (MailSendException ex) {
|
||||
ex.printStackTrace();
|
||||
assertEquals("host", sender.transport.getConnectedHost());
|
||||
assertEquals("username", sender.transport.getConnectedUsername());
|
||||
assertEquals("password", sender.transport.getConnectedPassword());
|
||||
assertTrue(sender.transport.isCloseCalled());
|
||||
assertEquals(1, sender.transport.getSentMessages().size());
|
||||
assertEquals(mimeMessage2, sender.transport.getSentMessage(0));
|
||||
assertEquals(1, ex.getFailedMessages().size());
|
||||
assertEquals(mimeMessage1, ex.getFailedMessages().keySet().iterator().next());
|
||||
Object subEx = ex.getFailedMessages().values().iterator().next();
|
||||
assertTrue(subEx instanceof MessagingException);
|
||||
assertEquals("failed", ((MessagingException) subEx).getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MockJavaMailSender extends JavaMailSenderImpl {
|
||||
|
||||
private MockTransport transport;
|
||||
|
||||
@Override
|
||||
protected Transport getTransport(Session session) throws NoSuchProviderException {
|
||||
this.transport = new MockTransport(session, null);
|
||||
return transport;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MockTransport extends Transport {
|
||||
|
||||
private String connectedHost = null;
|
||||
private int connectedPort = -2;
|
||||
private String connectedUsername = null;
|
||||
private String connectedPassword = null;
|
||||
private boolean closeCalled = false;
|
||||
private List<Message> sentMessages = new ArrayList<Message>();
|
||||
|
||||
private MockTransport(Session session, URLName urlName) {
|
||||
super(session, urlName);
|
||||
}
|
||||
|
||||
public String getConnectedHost() {
|
||||
return connectedHost;
|
||||
}
|
||||
|
||||
public int getConnectedPort() {
|
||||
return connectedPort;
|
||||
}
|
||||
|
||||
public String getConnectedUsername() {
|
||||
return connectedUsername;
|
||||
}
|
||||
|
||||
public String getConnectedPassword() {
|
||||
return connectedPassword;
|
||||
}
|
||||
|
||||
public boolean isCloseCalled() {
|
||||
return closeCalled;
|
||||
}
|
||||
|
||||
public List getSentMessages() {
|
||||
return sentMessages;
|
||||
}
|
||||
|
||||
public MimeMessage getSentMessage(int index) {
|
||||
return (MimeMessage) this.sentMessages.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(String host, int port, String username, String password) throws MessagingException {
|
||||
if (host == null) {
|
||||
throw new MessagingException("no host");
|
||||
}
|
||||
this.connectedHost = host;
|
||||
this.connectedPort = port;
|
||||
this.connectedUsername = username;
|
||||
this.connectedPassword = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws MessagingException {
|
||||
if ("".equals(connectedHost)) {
|
||||
throw new MessagingException("close failure");
|
||||
}
|
||||
this.closeCalled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Message message, Address[] addresses) throws MessagingException {
|
||||
if ("fail".equals(message.getSubject())) {
|
||||
throw new MessagingException("failed");
|
||||
}
|
||||
List addr1 = Arrays.asList(message.getAllRecipients());
|
||||
List addr2 = Arrays.asList(addresses);
|
||||
if (!addr1.equals(addr2)) {
|
||||
throw new MessagingException("addresses not correct");
|
||||
}
|
||||
if (message.getSentDate() == null) {
|
||||
throw new MessagingException("No sentDate specified");
|
||||
}
|
||||
if (message.getSubject() != null && message.getSubject().contains("custom")) {
|
||||
assertEquals(new Date(2005, 3, 1), message.getSentDate());
|
||||
}
|
||||
this.sentMessages.add(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 09.10.2004
|
||||
*/
|
||||
public class TestMethodInvokingTask {
|
||||
|
||||
public int counter = 0;
|
||||
|
||||
private Object lock = new Object();
|
||||
|
||||
public void doSomething() {
|
||||
this.counter++;
|
||||
}
|
||||
|
||||
public void doWait() {
|
||||
this.counter++;
|
||||
// wait until stop is called
|
||||
synchronized (this.lock) {
|
||||
try {
|
||||
this.lock.wait();
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
synchronized(this.lock) {
|
||||
this.lock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/** @author Rob Harrop */
|
||||
public class CronTriggerBeanTests {
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testInvalidStartDelay() {
|
||||
createTriggerBean().setStartDelay(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartTime() throws Exception {
|
||||
CronTriggerBean bean = createTriggerBean();
|
||||
Date startTime = new Date(System.currentTimeMillis() + 1234L);
|
||||
bean.setStartTime(startTime);
|
||||
bean.afterPropertiesSet();
|
||||
assertTimeEquals(startTime, bean.getStartTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartDelay() throws Exception {
|
||||
CronTriggerBean bean = createTriggerBean();
|
||||
long startDelay = 1234L;
|
||||
Date startTime = new Date(System.currentTimeMillis() + startDelay);
|
||||
bean.setStartDelay(startDelay);
|
||||
bean.afterPropertiesSet();
|
||||
assertTimeEquals(startTime, bean.getStartTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartDelayOverridesStartTime() throws Exception {
|
||||
CronTriggerBean bean = createTriggerBean();
|
||||
long startDelay = 1234L;
|
||||
Date startTime = new Date(System.currentTimeMillis() + startDelay);
|
||||
bean.setStartTime(new Date(System.currentTimeMillis() + 123456789L));
|
||||
bean.setStartDelay(startDelay);
|
||||
bean.afterPropertiesSet();
|
||||
assertTimeEquals(startTime, bean.getStartTime());
|
||||
}
|
||||
|
||||
private CronTriggerBean createTriggerBean() {
|
||||
CronTriggerBean triggerBean = new CronTriggerBean();
|
||||
triggerBean.setName("test");
|
||||
return triggerBean;
|
||||
}
|
||||
|
||||
private void assertTimeEquals(Date a, Date b) {
|
||||
Calendar ca = Calendar.getInstance();
|
||||
ca.setTime(a);
|
||||
ca.set(Calendar.MILLISECOND, 0);
|
||||
Calendar cb = Calendar.getInstance();
|
||||
cb.setTime(b);
|
||||
cb.set(Calendar.MILLISECOND, 0);
|
||||
assertEquals(ca, cb);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public class QuartzSchedulerLifecycleTests {
|
||||
|
||||
@Test // SPR-6354
|
||||
public void destroyLazyInitSchedulerWithDefaultShutdownOrderDoesNotHang() {
|
||||
AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartzSchedulerLifecycleTests.xml", this.getClass());
|
||||
assertNotNull(context.getBean("lazyInitSchedulerWithDefaultShutdownOrder"));
|
||||
StopWatch sw = new StopWatch();
|
||||
sw.start("lazyScheduler");
|
||||
context.destroy();
|
||||
sw.stop();
|
||||
assertTrue("Quartz Scheduler with lazy-init is hanging on destruction: " +
|
||||
sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 500);
|
||||
}
|
||||
|
||||
@Test // SPR-6354
|
||||
public void destroyLazyInitSchedulerWithCustomShutdownOrderDoesNotHang() {
|
||||
AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartzSchedulerLifecycleTests.xml", this.getClass());
|
||||
assertNotNull(context.getBean("lazyInitSchedulerWithCustomShutdownOrder"));
|
||||
StopWatch sw = new StopWatch();
|
||||
sw.start("lazyScheduler");
|
||||
context.destroy();
|
||||
sw.stop();
|
||||
assertTrue("Quartz Scheduler with lazy-init is hanging on destruction: " +
|
||||
sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 500);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2006 the original author or authors.
|
||||
*
|
||||
* 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.springframework.scheduling.quartz;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
*/
|
||||
public class QuartzTestBean {
|
||||
|
||||
private int importCount;
|
||||
|
||||
private int exportCount;
|
||||
|
||||
|
||||
public void doImport() {
|
||||
++importCount;
|
||||
}
|
||||
|
||||
public void doExport() {
|
||||
++exportCount;
|
||||
}
|
||||
|
||||
public int getImportCount() {
|
||||
return importCount;
|
||||
}
|
||||
|
||||
public int getExportCount() {
|
||||
return exportCount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.jasperreports;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import net.sf.jasperreports.engine.JRDataSource;
|
||||
import net.sf.jasperreports.engine.JRParameter;
|
||||
import net.sf.jasperreports.engine.JasperFillManager;
|
||||
import net.sf.jasperreports.engine.JasperPrint;
|
||||
import net.sf.jasperreports.engine.JasperReport;
|
||||
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
|
||||
import net.sf.jasperreports.engine.export.JRCsvExporterParameter;
|
||||
import net.sf.jasperreports.engine.export.JRExportProgressMonitor;
|
||||
import net.sf.jasperreports.engine.export.JRHtmlExporter;
|
||||
import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
|
||||
import net.sf.jasperreports.engine.export.JRPdfExporter;
|
||||
import net.sf.jasperreports.engine.export.JRPdfExporterParameter;
|
||||
import net.sf.jasperreports.engine.export.JRXlsExporterParameter;
|
||||
import net.sf.jasperreports.engine.util.JRLoader;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @since 18.11.2004
|
||||
*/
|
||||
public class JasperReportsUtilsTests extends TestCase {
|
||||
|
||||
public void testRenderAsCsvWithDataSource() throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
JasperReportsUtils.renderAsCsv(getReport(), getParameters(), getDataSource(), writer);
|
||||
String output = writer.getBuffer().toString();
|
||||
assertCsvOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsCsvWithCollection() throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
JasperReportsUtils.renderAsCsv(getReport(), getParameters(), getData(), writer);
|
||||
String output = writer.getBuffer().toString();
|
||||
assertCsvOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsCsvWithExporterParameters() throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
Map exporterParameters = new HashMap();
|
||||
exporterParameters.put(JRCsvExporterParameter.FIELD_DELIMITER, "~");
|
||||
JasperReportsUtils.renderAsCsv(getReport(), getParameters(), getData(), writer, exporterParameters);
|
||||
String output = writer.getBuffer().toString();
|
||||
assertCsvOutputCorrect(output);
|
||||
assertTrue("Delimiter is incorrect", output.indexOf("~") > -1);
|
||||
}
|
||||
|
||||
public void testRenderAsHtmlWithDataSource() throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
JasperReportsUtils.renderAsHtml(getReport(), getParameters(), getDataSource(), writer);
|
||||
String output = writer.getBuffer().toString();
|
||||
assertHtmlOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsHtmlWithCollection() throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
JasperReportsUtils.renderAsHtml(getReport(), getParameters(), getData(), writer);
|
||||
String output = writer.getBuffer().toString();
|
||||
assertHtmlOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsHtmlWithExporterParameters() throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
Map exporterParameters = new HashMap();
|
||||
String uri = "/my/uri";
|
||||
exporterParameters.put(JRHtmlExporterParameter.IMAGES_URI, uri);
|
||||
JasperReportsUtils.renderAsHtml(getReport(), getParameters(), getData(), writer, exporterParameters);
|
||||
String output = writer.getBuffer().toString();
|
||||
assertHtmlOutputCorrect(output);
|
||||
assertTrue("URI not included", output.indexOf(uri) > -1);
|
||||
}
|
||||
|
||||
public void testRenderAsPdfWithDataSource() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
JasperReportsUtils.renderAsPdf(getReport(), getParameters(), getDataSource(), os);
|
||||
byte[] output = os.toByteArray();
|
||||
assertPdfOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsPdfWithCollection() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
JasperReportsUtils.renderAsPdf(getReport(), getParameters(), getData(), os);
|
||||
byte[] output = os.toByteArray();
|
||||
assertPdfOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsPdfWithExporterParameters() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
Map exporterParameters = new HashMap();
|
||||
exporterParameters.put(JRPdfExporterParameter.PDF_VERSION, JRPdfExporterParameter.PDF_VERSION_1_6.toString());
|
||||
JasperReportsUtils.renderAsPdf(getReport(), getParameters(), getData(), os, exporterParameters);
|
||||
byte[] output = os.toByteArray();
|
||||
assertPdfOutputCorrect(output);
|
||||
assertTrue(new String(output).indexOf("PDF-1.6") > -1);
|
||||
}
|
||||
|
||||
public void testRenderAsXlsWithDataSource() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
JasperReportsUtils.renderAsXls(getReport(), getParameters(), getDataSource(), os);
|
||||
byte[] output = os.toByteArray();
|
||||
assertXlsOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsXlsWithCollection() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
JasperReportsUtils.renderAsXls(getReport(), getParameters(), getData(), os);
|
||||
byte[] output = os.toByteArray();
|
||||
assertXlsOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderAsXlsWithExporterParameters() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
Map exporterParameters = new HashMap();
|
||||
|
||||
SimpleProgressMonitor monitor = new SimpleProgressMonitor();
|
||||
exporterParameters.put(JRXlsExporterParameter.PROGRESS_MONITOR, monitor);
|
||||
|
||||
JasperReportsUtils.renderAsXls(getReport(), getParameters(), getData(), os, exporterParameters);
|
||||
byte[] output = os.toByteArray();
|
||||
assertXlsOutputCorrect(output);
|
||||
assertTrue(monitor.isInvoked());
|
||||
}
|
||||
|
||||
public void testRenderWithWriter() throws Exception {
|
||||
StringWriter writer = new StringWriter();
|
||||
JasperPrint print = JasperFillManager.fillReport(getReport(), getParameters(), getDataSource());
|
||||
JasperReportsUtils.render(new JRHtmlExporter(), print, writer);
|
||||
String output = writer.getBuffer().toString();
|
||||
assertHtmlOutputCorrect(output);
|
||||
}
|
||||
|
||||
public void testRenderWithOutputStream() throws Exception {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
JasperPrint print = JasperFillManager.fillReport(getReport(), getParameters(), getDataSource());
|
||||
JasperReportsUtils.render(new JRPdfExporter(), print, os);
|
||||
byte[] output = os.toByteArray();
|
||||
assertPdfOutputCorrect(output);
|
||||
}
|
||||
|
||||
private void assertCsvOutputCorrect(String output) {
|
||||
assertTrue("Output length should be greater than 0", (output.length() > 0));
|
||||
assertTrue("Output should start with Dear Lord!", output.startsWith("Dear Lord!"));
|
||||
assertTrue("Output should contain 'MeineSeite'", output.indexOf("MeineSeite") > -1);
|
||||
}
|
||||
|
||||
private void assertHtmlOutputCorrect(String output) {
|
||||
assertTrue("Output length should be greater than 0", (output.length() > 0));
|
||||
assertTrue("Output should contain <html>", output.indexOf("<html>") > -1);
|
||||
assertTrue("Output should contain 'MeineSeite'", output.indexOf("MeineSeite") > -1);
|
||||
}
|
||||
|
||||
private void assertPdfOutputCorrect(byte[] output) throws Exception {
|
||||
assertTrue("Output length should be greater than 0", (output.length > 0));
|
||||
|
||||
String translated = new String(output, "US-ASCII");
|
||||
assertTrue("Output should start with %PDF", translated.startsWith("%PDF"));
|
||||
}
|
||||
|
||||
private void assertXlsOutputCorrect(byte[] output) throws Exception {
|
||||
HSSFWorkbook workbook = new HSSFWorkbook(new ByteArrayInputStream(output));
|
||||
HSSFSheet sheet = workbook.getSheetAt(0);
|
||||
assertNotNull("Sheet should not be null", sheet);
|
||||
HSSFRow row = sheet.getRow(3);
|
||||
HSSFCell cell = row.getCell((short) 1);
|
||||
assertNotNull("Cell should not be null", cell);
|
||||
assertEquals("Cell content should be Dear Lord!", "Dear Lord!", cell.getStringCellValue());
|
||||
}
|
||||
|
||||
private JasperReport getReport() throws Exception {
|
||||
ClassPathResource resource = new ClassPathResource("DataSourceReport.jasper", getClass());
|
||||
return (JasperReport) JRLoader.loadObject(resource.getInputStream());
|
||||
}
|
||||
|
||||
private Map getParameters() {
|
||||
Map model = new HashMap();
|
||||
model.put("ReportTitle", "Dear Lord!");
|
||||
model.put(JRParameter.REPORT_LOCALE, Locale.GERMAN);
|
||||
model.put(JRParameter.REPORT_RESOURCE_BUNDLE,
|
||||
ResourceBundle.getBundle("org/springframework/ui/jasperreports/messages", Locale.GERMAN));
|
||||
return model;
|
||||
}
|
||||
|
||||
private JRDataSource getDataSource() {
|
||||
return new JRBeanCollectionDataSource(getData());
|
||||
}
|
||||
|
||||
private List getData() {
|
||||
List list = new ArrayList();
|
||||
for (int x = 0; x < 10; x++) {
|
||||
PersonBean bean = new PersonBean();
|
||||
bean.setId(x);
|
||||
bean.setName("Rob Harrop");
|
||||
bean.setStreet("foo");
|
||||
list.add(bean);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
private static class SimpleProgressMonitor implements JRExportProgressMonitor {
|
||||
|
||||
private boolean invoked = false;
|
||||
|
||||
public void afterPageExport() {
|
||||
this.invoked = true;
|
||||
}
|
||||
|
||||
public boolean isInvoked() {
|
||||
return invoked;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.jasperreports;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
*/
|
||||
public class PersonBean {
|
||||
|
||||
private int id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String street;
|
||||
|
||||
private String city;
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public void setStreet(String street) {
|
||||
this.street = street;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2005 the original author or authors.
|
||||
*
|
||||
* 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.springframework.ui.jasperreports;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
*/
|
||||
public class ProductBean {
|
||||
|
||||
private int id;
|
||||
|
||||
private String name;
|
||||
|
||||
private float quantity;
|
||||
|
||||
private float price;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public float getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(float quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public float getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(float price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||
|
||||
<!-- Appenders -->
|
||||
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
||||
<param name="Target" value="System.out" />
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.beans">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.binding">
|
||||
<level value="debug" />
|
||||
</logger>
|
||||
|
||||
<!-- Root Logger -->
|
||||
<root>
|
||||
<priority value="warn" />
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</log4j:configuration>
|
||||
@@ -1,4 +0,0 @@
|
||||
text/foo foo
|
||||
text/bar bar
|
||||
image/foo fimg
|
||||
image/bar bimg
|
||||
@@ -1,33 +0,0 @@
|
||||
<?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:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="scheduler"
|
||||
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
|
||||
<property name="triggers" ref="trigger" />
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
<bean id="trigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<property name="repeatInterval" value="1000" />
|
||||
<property name="repeatCount" value="1" />
|
||||
<property name="jobDetail">
|
||||
<bean class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||
<property name="jobDataAsMap">
|
||||
<map>
|
||||
<entry key="param" value="10" />
|
||||
</map>
|
||||
</property>
|
||||
<property name="jobClass"
|
||||
value="org.springframework.scheduling.quartz.QuartzSupportTests$DummyJob" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL">
|
||||
<jdbc:script location="org/springframework/scheduling/quartz/quartz-hsql.sql" />
|
||||
</jdbc:embedded-database>
|
||||
|
||||
</beans>
|
||||
@@ -1,29 +0,0 @@
|
||||
<quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.opensymphony.com/quartz/JobSchedulingData
|
||||
http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd"
|
||||
version="1.5">
|
||||
|
||||
<job>
|
||||
<job-detail>
|
||||
<name>myJob</name>
|
||||
<group>myGroup</group>
|
||||
<job-class>org.springframework.scheduling.quartz.QuartzSupportTests$DummyJob</job-class>
|
||||
<job-data-map>
|
||||
<entry>
|
||||
<key>param</key>
|
||||
<value>10</value>
|
||||
</entry>
|
||||
</job-data-map>
|
||||
</job-detail>
|
||||
<trigger>
|
||||
<simple>
|
||||
<name>myTrigger</name>
|
||||
<group>myGroup</group>
|
||||
<repeat-count>1</repeat-count>
|
||||
<repeat-interval>500</repeat-interval>
|
||||
</simple>
|
||||
</trigger>
|
||||
</job>
|
||||
|
||||
</quartz>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
|
||||
<property name="triggers">
|
||||
<list>
|
||||
<ref local="exportTrigger"/>
|
||||
<ref local="importTrigger"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="exportTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<property name="jobDetail">
|
||||
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
||||
<property name="targetObject" ref="exportService"/>
|
||||
<property name="targetMethod" value="doExport"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="repeatInterval" value="1000"/>
|
||||
<property name="repeatCount" value="1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="importTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<property name="jobDetail">
|
||||
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
||||
<property name="targetObject" ref="importService"/>
|
||||
<property name="targetMethod" value="doImport"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="repeatInterval" value="1000"/>
|
||||
<property name="repeatCount" value="1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="exportService" class="org.springframework.scheduling.quartz.QuartzTestBean"/>
|
||||
|
||||
<bean id="importService" class="org.springframework.scheduling.quartz.QuartzTestBean"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="scheduler1" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
|
||||
<property name="schedulerName" value="quartz1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="scheduler2" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
|
||||
<property name="schedulerName" value="quartz2"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -1,152 +0,0 @@
|
||||
DROP TABLE qrtz_locks IF EXISTS;
|
||||
DROP TABLE qrtz_scheduler_state IF EXISTS;
|
||||
DROP TABLE qrtz_fired_triggers IF EXISTS;
|
||||
DROP TABLE qrtz_paused_trigger_grps IF EXISTS;
|
||||
DROP TABLE qrtz_calendars IF EXISTS;
|
||||
DROP TABLE qrtz_trigger_listeners IF EXISTS;
|
||||
DROP TABLE qrtz_blob_triggers IF EXISTS;
|
||||
DROP TABLE qrtz_cron_triggers IF EXISTS;
|
||||
DROP TABLE qrtz_simple_triggers IF EXISTS;
|
||||
DROP TABLE qrtz_triggers IF EXISTS;
|
||||
DROP TABLE qrtz_job_listeners IF EXISTS;
|
||||
DROP TABLE qrtz_job_details IF EXISTS;
|
||||
|
||||
CREATE TABLE qrtz_job_details
|
||||
(
|
||||
JOB_NAME VARCHAR(200) NOT NULL,
|
||||
JOB_GROUP VARCHAR(200) NOT NULL,
|
||||
DESCRIPTION VARCHAR(250) NULL,
|
||||
JOB_CLASS_NAME VARCHAR(250) NOT NULL,
|
||||
IS_DURABLE VARCHAR(1) NOT NULL,
|
||||
IS_VOLATILE VARCHAR(1) NOT NULL,
|
||||
IS_STATEFUL VARCHAR(1) NOT NULL,
|
||||
REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
|
||||
JOB_DATA BINARY NULL,
|
||||
PRIMARY KEY (JOB_NAME,JOB_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_job_listeners
|
||||
(
|
||||
JOB_NAME VARCHAR(200) NOT NULL,
|
||||
JOB_GROUP VARCHAR(200) NOT NULL,
|
||||
JOB_LISTENER VARCHAR(200) NOT NULL,
|
||||
PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER),
|
||||
FOREIGN KEY (JOB_NAME,JOB_GROUP)
|
||||
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_triggers
|
||||
(
|
||||
TRIGGER_NAME VARCHAR(200) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
JOB_NAME VARCHAR(200) NOT NULL,
|
||||
JOB_GROUP VARCHAR(200) NOT NULL,
|
||||
IS_VOLATILE VARCHAR(1) NOT NULL,
|
||||
DESCRIPTION VARCHAR(250) NULL,
|
||||
NEXT_FIRE_TIME NUMERIC(13) NULL,
|
||||
PREV_FIRE_TIME NUMERIC(13) NULL,
|
||||
PRIORITY INTEGER NULL,
|
||||
TRIGGER_STATE VARCHAR(16) NOT NULL,
|
||||
TRIGGER_TYPE VARCHAR(8) NOT NULL,
|
||||
START_TIME NUMERIC(13) NOT NULL,
|
||||
END_TIME NUMERIC(13) NULL,
|
||||
CALENDAR_NAME VARCHAR(200) NULL,
|
||||
MISFIRE_INSTR NUMERIC(2) NULL,
|
||||
JOB_DATA BINARY NULL,
|
||||
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
|
||||
FOREIGN KEY (JOB_NAME,JOB_GROUP)
|
||||
REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_simple_triggers
|
||||
(
|
||||
TRIGGER_NAME VARCHAR(200) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
REPEAT_COUNT NUMERIC(7) NOT NULL,
|
||||
REPEAT_INTERVAL NUMERIC(12) NOT NULL,
|
||||
TIMES_TRIGGERED NUMERIC(7) NOT NULL,
|
||||
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
|
||||
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_cron_triggers
|
||||
(
|
||||
TRIGGER_NAME VARCHAR(200) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
CRON_EXPRESSION VARCHAR(120) NOT NULL,
|
||||
TIME_ZONE_ID VARCHAR(80),
|
||||
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
|
||||
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_blob_triggers
|
||||
(
|
||||
TRIGGER_NAME VARCHAR(200) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
BLOB_DATA BINARY NULL,
|
||||
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
|
||||
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_trigger_listeners
|
||||
(
|
||||
TRIGGER_NAME VARCHAR(200) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
TRIGGER_LISTENER VARCHAR(200) NOT NULL,
|
||||
PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER),
|
||||
FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
|
||||
REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_calendars
|
||||
(
|
||||
CALENDAR_NAME VARCHAR(200) NOT NULL,
|
||||
CALENDAR BINARY NOT NULL,
|
||||
PRIMARY KEY (CALENDAR_NAME)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_paused_trigger_grps
|
||||
(
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
PRIMARY KEY (TRIGGER_GROUP)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_fired_triggers
|
||||
(
|
||||
ENTRY_ID VARCHAR(95) NOT NULL,
|
||||
TRIGGER_NAME VARCHAR(200) NOT NULL,
|
||||
TRIGGER_GROUP VARCHAR(200) NOT NULL,
|
||||
IS_VOLATILE VARCHAR(1) NOT NULL,
|
||||
INSTANCE_NAME VARCHAR(200) NOT NULL,
|
||||
FIRED_TIME NUMERIC(13) NOT NULL,
|
||||
PRIORITY INTEGER NOT NULL,
|
||||
STATE VARCHAR(16) NOT NULL,
|
||||
JOB_NAME VARCHAR(200) NULL,
|
||||
JOB_GROUP VARCHAR(200) NULL,
|
||||
IS_STATEFUL VARCHAR(1) NULL,
|
||||
REQUESTS_RECOVERY VARCHAR(1) NULL,
|
||||
PRIMARY KEY (ENTRY_ID)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_scheduler_state
|
||||
(
|
||||
INSTANCE_NAME VARCHAR(200) NOT NULL,
|
||||
LAST_CHECKIN_TIME NUMERIC(13) NOT NULL,
|
||||
CHECKIN_INTERVAL NUMERIC(13) NOT NULL,
|
||||
PRIMARY KEY (INSTANCE_NAME)
|
||||
);
|
||||
|
||||
CREATE TABLE qrtz_locks
|
||||
(
|
||||
LOCK_NAME VARCHAR(40) NOT NULL,
|
||||
PRIMARY KEY (LOCK_NAME)
|
||||
);
|
||||
|
||||
INSERT INTO qrtz_locks values('TRIGGER_ACCESS');
|
||||
INSERT INTO qrtz_locks values('JOB_ACCESS');
|
||||
INSERT INTO qrtz_locks values('CALENDAR_ACCESS');
|
||||
INSERT INTO qrtz_locks values('STATE_ACCESS');
|
||||
INSERT INTO qrtz_locks values('MISFIRE_ACCESS');
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="lazyInitSchedulerWithDefaultShutdownOrder" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="true"/>
|
||||
|
||||
<bean id="lazyInitSchedulerWithCustomShutdownOrder" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="true">
|
||||
<property name="phase" value="99"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"/>
|
||||
|
||||
<bean class="org.springframework.scheduling.quartz.SchedulerAccessorBean">
|
||||
<property name="scheduler" ref="scheduler"/>
|
||||
<property name="triggers">
|
||||
<list>
|
||||
<ref local="exportTrigger"/>
|
||||
<ref local="importTrigger"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="exportTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<property name="jobDetail">
|
||||
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
||||
<property name="targetObject" ref="exportService"/>
|
||||
<property name="targetMethod" value="doExport"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="repeatInterval" value="1000"/>
|
||||
<property name="repeatCount" value="1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="importTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<property name="jobDetail">
|
||||
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
||||
<property name="targetObject" ref="importService"/>
|
||||
<property name="targetMethod" value="doImport"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="repeatInterval" value="1000"/>
|
||||
<property name="repeatCount" value="1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="exportService" class="org.springframework.scheduling.quartz.QuartzTestBean"/>
|
||||
|
||||
<bean id="importService" class="org.springframework.scheduling.quartz.QuartzTestBean"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
|
||||
<property name="schedulerName" value="myScheduler"/>
|
||||
<property name="exposeSchedulerInRepository" value="true"/>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.scheduling.quartz.SchedulerAccessorBean">
|
||||
<property name="schedulerName" value="myScheduler"/>
|
||||
<property name="triggers">
|
||||
<list>
|
||||
<ref local="exportTrigger"/>
|
||||
<ref local="importTrigger"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="exportTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<property name="jobDetail">
|
||||
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
||||
<property name="targetObject" ref="exportService"/>
|
||||
<property name="targetMethod" value="doExport"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="repeatInterval" value="1000"/>
|
||||
<property name="repeatCount" value="1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="importTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<property name="jobDetail">
|
||||
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
|
||||
<property name="targetObject" ref="importService"/>
|
||||
<property name="targetMethod" value="doImport"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="repeatInterval" value="1000"/>
|
||||
<property name="repeatCount" value="1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="exportService" class="org.springframework.scheduling.quartz.QuartzTestBean"/>
|
||||
|
||||
<bean id="importService" class="org.springframework.scheduling.quartz.QuartzTestBean"/>
|
||||
|
||||
</beans>
|
||||
Binary file not shown.
@@ -1,185 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created using JasperAssistant (http://www.jasperassistant.com) -->
|
||||
<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
|
||||
|
||||
<jasperReport name="DataSourceReport" pageWidth="595" pageHeight="842" columnWidth="515" leftMargin="40" rightMargin="40" topMargin="50" bottomMargin="50">
|
||||
<reportFont name="Arial_Normal" isDefault="true" fontName="Arial" size="12" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<reportFont name="Arial_Bold" isDefault="false" fontName="Arial" size="12" isBold="true" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica-Bold" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<reportFont name="Arial_Italic" isDefault="false" fontName="Arial" size="12" isBold="false" isItalic="true" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica-Oblique" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<parameter name="ReportTitle" class="java.lang.String">
|
||||
</parameter>
|
||||
<parameter name="DataFile" class="java.lang.String">
|
||||
</parameter>
|
||||
<field name="id" class="java.lang.Integer">
|
||||
</field>
|
||||
<field name="name" class="java.lang.String">
|
||||
</field>
|
||||
<field name="street" class="java.lang.String">
|
||||
</field>
|
||||
<field name="city" class="java.lang.String">
|
||||
</field>
|
||||
<variable name="CityNumber" class="java.lang.Integer" resetType="Group" resetGroup="CityGroup" calculation="System">
|
||||
<initialValueExpression><![CDATA[($V{CityNumber} != null)?(new Integer($V{CityNumber}.intValue() + 1)):(new Integer(1))]]></initialValueExpression>
|
||||
</variable>
|
||||
<group name="CityGroup" minHeightToStartNewPage="60">
|
||||
<groupExpression><![CDATA[$F{city}]]></groupExpression>
|
||||
<groupHeader>
|
||||
<band height="20">
|
||||
<rectangle>
|
||||
<reportElement x="0" y="4" width="515" height="15" forecolor="#c0c0c0" backcolor="#c0c0c0"/>
|
||||
<graphicElement/>
|
||||
</rectangle>
|
||||
<textField>
|
||||
<reportElement mode="Opaque" x="0" y="4" width="515" height="15" backcolor="#c0c0c0"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[" " + String.valueOf($V{CityNumber}) + ". " + String.valueOf($F{city})]]></textFieldExpression>
|
||||
</textField>
|
||||
<line>
|
||||
<reportElement x="0" y="19" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
</band>
|
||||
</groupHeader>
|
||||
<groupFooter>
|
||||
<band height="20">
|
||||
<line>
|
||||
<reportElement x="0" y="-1" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
<staticText>
|
||||
<reportElement x="400" y="1" width="60" height="15"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Count :]]></text>
|
||||
</staticText>
|
||||
<textField>
|
||||
<reportElement x="460" y="1" width="30" height="15"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.Integer"><![CDATA[$V{CityGroup_COUNT}]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</groupFooter>
|
||||
</group>
|
||||
<title>
|
||||
<band height="70">
|
||||
<line>
|
||||
<reportElement x="0" y="0" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
<textField isBlankWhenNull="true">
|
||||
<reportElement x="0" y="10" width="515" height="30"/>
|
||||
<textElement textAlignment="Center">
|
||||
<font reportFont="Arial_Normal" size="22"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$P{ReportTitle}]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField isBlankWhenNull="true">
|
||||
<reportElement x="0" y="40" width="515" height="20"/>
|
||||
<textElement textAlignment="Center">
|
||||
<font reportFont="Arial_Normal" size="14"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$P{DataFile}]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</title>
|
||||
<pageHeader>
|
||||
<band height="20">
|
||||
<rectangle>
|
||||
<reportElement x="0" y="5" width="515" height="15" forecolor="#333333" backcolor="#333333"/>
|
||||
<graphicElement/>
|
||||
</rectangle>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="0" y="5" width="55" height="15" forecolor="#ffffff" backcolor="#333333"/>
|
||||
<textElement textAlignment="Center">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[ID]]></text>
|
||||
</staticText>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="55" y="5" width="205" height="15" forecolor="#ffffff" backcolor="#333333"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Name]]></text>
|
||||
</staticText>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="260" y="5" width="255" height="15" forecolor="#ffffff" backcolor="#333333"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Street]]></text>
|
||||
</staticText>
|
||||
</band>
|
||||
</pageHeader>
|
||||
<detail>
|
||||
<band height="20">
|
||||
<textField>
|
||||
<reportElement x="0" y="4" width="50" height="15"/>
|
||||
<textElement textAlignment="Right"/>
|
||||
<textFieldExpression class="java.lang.Integer"><![CDATA[$F{id}]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField isStretchWithOverflow="true">
|
||||
<reportElement positionType="Float" x="55" y="4" width="200" height="15"/>
|
||||
<textElement/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$F{name}]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField isStretchWithOverflow="true">
|
||||
<reportElement positionType="Float" x="260" y="4" width="255" height="15"/>
|
||||
<textElement/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$F{street}]]></textFieldExpression>
|
||||
</textField>
|
||||
<line>
|
||||
<reportElement positionType="Float" x="0" y="19" width="515" height="1" forecolor="#808080"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
</band>
|
||||
</detail>
|
||||
<pageFooter>
|
||||
<band height="40">
|
||||
<line>
|
||||
<reportElement x="0" y="10" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
<textField>
|
||||
<reportElement x="200" y="20" width="80" height="15"/>
|
||||
<textElement textAlignment="Right"/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$R{page} + " " + String.valueOf($V{PAGE_NUMBER}) + " of"]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField evaluationTime="Report">
|
||||
<reportElement x="280" y="20" width="75" height="15"/>
|
||||
<textElement/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[" " + String.valueOf($V{PAGE_NUMBER})]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</pageFooter>
|
||||
<lastPageFooter>
|
||||
<band height="60">
|
||||
<textField>
|
||||
<reportElement x="0" y="10" width="515" height="15"/>
|
||||
<textElement textAlignment="Center"/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA["There were " +
|
||||
String.valueOf($V{REPORT_COUNT}) +
|
||||
" address records on this report."]]></textFieldExpression>
|
||||
</textField>
|
||||
<line>
|
||||
<reportElement x="0" y="30" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
<textField>
|
||||
<reportElement x="200" y="40" width="80" height="15"/>
|
||||
<textElement textAlignment="Right"/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$R{page} + " " + String.valueOf($V{PAGE_NUMBER}) + " of"]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField evaluationTime="Report">
|
||||
<reportElement x="280" y="40" width="75" height="15"/>
|
||||
<textElement/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[" " + String.valueOf($V{PAGE_NUMBER})]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</lastPageFooter>
|
||||
</jasperReport>
|
||||
@@ -1 +0,0 @@
|
||||
page=MeineSeite
|
||||
Binary file not shown.
@@ -1,227 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created using JasperAssistant (http://www.jasperassistant.com) -->
|
||||
<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
|
||||
|
||||
<jasperReport name="ProductReport" columnCount="2" pageWidth="325" pageHeight="842" columnWidth="160" columnSpacing="5" leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0">
|
||||
<reportFont name="Arial_Normal" isDefault="true" fontName="Arial" size="8" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<reportFont name="Arial_Bold" isDefault="false" fontName="Arial" size="8" isBold="true" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica-Bold" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<reportFont name="Arial_Italic" isDefault="false" fontName="Arial" size="8" isBold="false" isItalic="true" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica-Oblique" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<parameter name="City" class="java.lang.String"/>
|
||||
<field name="id" class="java.lang.Integer">
|
||||
</field>
|
||||
<field name="name" class="java.lang.String">
|
||||
</field>
|
||||
<field name="quantity" class="java.lang.Float">
|
||||
</field>
|
||||
<field name="price" class="java.lang.Float">
|
||||
</field>
|
||||
<variable name="QuantityProductSum" class="java.lang.Float" resetType="Group" resetGroup="ProductGroup" calculation="Sum">
|
||||
<variableExpression><![CDATA[$F{quantity}]]></variableExpression>
|
||||
</variable>
|
||||
<variable name="PriceProductSum" class="java.lang.Float" resetType="Group" resetGroup="ProductGroup" calculation="Sum">
|
||||
<variableExpression><![CDATA[$F{price}]]></variableExpression>
|
||||
</variable>
|
||||
<variable name="QuantitySum" class="java.lang.Float" calculation="Sum">
|
||||
<variableExpression><![CDATA[$F{quantity}]]></variableExpression>
|
||||
</variable>
|
||||
<variable name="PriceSum" class="java.lang.Float" calculation="Sum">
|
||||
<variableExpression><![CDATA[$F{price}]]></variableExpression>
|
||||
</variable>
|
||||
<variable name="ProductCount" class="java.lang.Integer" resetType="Group" resetGroup="ProductGroup" calculation="System">
|
||||
<initialValueExpression><![CDATA[($V{ProductCount} != null)?(new Integer($V{ProductCount}.intValue() + 1)):(new Integer(1))]]></initialValueExpression>
|
||||
</variable>
|
||||
<group name="ProductGroup">
|
||||
<groupExpression><![CDATA[$F{id}]]></groupExpression>
|
||||
<groupHeader>
|
||||
<band height="14">
|
||||
<textField>
|
||||
<reportElement x="0" y="2" width="15" height="10"/>
|
||||
<textElement textAlignment="Right"/>
|
||||
<textFieldExpression class="java.lang.Integer"><![CDATA[$F{id}]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField isStretchWithOverflow="true">
|
||||
<reportElement positionType="Float" x="20" y="2" width="80" height="10"/>
|
||||
<textElement/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$F{name}]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField isStretchWithOverflow="true" evaluationTime="Group" evaluationGroup="ProductGroup" pattern="#0">
|
||||
<reportElement positionType="Float" x="105" y="2" width="20" height="10"/>
|
||||
<textElement textAlignment="Right"/>
|
||||
<textFieldExpression class="java.lang.Float"><![CDATA[$V{QuantityProductSum}]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField isStretchWithOverflow="true" evaluationTime="Group" evaluationGroup="ProductGroup" pattern="#0.00">
|
||||
<reportElement positionType="Float" x="130" y="2" width="30" height="10"/>
|
||||
<textElement textAlignment="Right"/>
|
||||
<textFieldExpression class="java.lang.Float"><![CDATA[$V{PriceProductSum}]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</groupHeader>
|
||||
<groupFooter>
|
||||
<band>
|
||||
</band>
|
||||
</groupFooter>
|
||||
</group>
|
||||
<title>
|
||||
<band height="14">
|
||||
<staticText>
|
||||
<reportElement x="0" y="2" width="60" height="10"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Italic"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Title]]></text>
|
||||
</staticText>
|
||||
<textField>
|
||||
<reportElement x="0" y="2" width="325" height="10"/>
|
||||
<textElement textAlignment="Center">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA["Products ordered by people in " + $P{City}]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</title>
|
||||
<pageHeader>
|
||||
<band height="14">
|
||||
<rectangle>
|
||||
<reportElement mode="Transparent" x="0" y="2" width="325" height="10" forecolor="#808000"/>
|
||||
<graphicElement pen="Thin"/>
|
||||
</rectangle>
|
||||
<staticText>
|
||||
<reportElement x="0" y="2" width="60" height="10" forecolor="#808000"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Italic"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Page Header]]></text>
|
||||
</staticText>
|
||||
</band>
|
||||
</pageHeader>
|
||||
<columnHeader>
|
||||
<band height="14">
|
||||
<rectangle>
|
||||
<reportElement x="0" y="2" width="160" height="10" forecolor="#ffff99" backcolor="#ffff99"/>
|
||||
<graphicElement/>
|
||||
</rectangle>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="0" y="2" width="20" height="10" backcolor="#ffff99"/>
|
||||
<textElement textAlignment="Center">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[ID]]></text>
|
||||
</staticText>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="20" y="2" width="85" height="10" backcolor="#ffff99"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Name]]></text>
|
||||
</staticText>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="105" y="2" width="20" height="10" backcolor="#ffff99"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Qty]]></text>
|
||||
</staticText>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="125" y="2" width="35" height="10" backcolor="#ffff99"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Price]]></text>
|
||||
</staticText>
|
||||
</band>
|
||||
</columnHeader>
|
||||
<columnFooter>
|
||||
<band height="14">
|
||||
<line>
|
||||
<reportElement x="0" y="1" width="160" height="1"/>
|
||||
<graphicElement pen="Thin"/>
|
||||
</line>
|
||||
<staticText>
|
||||
<reportElement x="0" y="2" width="60" height="10"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Italic"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Column Footer]]></text>
|
||||
</staticText>
|
||||
<staticText>
|
||||
<reportElement x="70" y="2" width="30" height="10"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Total :]]></text>
|
||||
</staticText>
|
||||
<textField pattern="#0">
|
||||
<reportElement x="105" y="2" width="20" height="10"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.Float"><![CDATA[$V{QuantitySum}]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField pattern="#0.00">
|
||||
<reportElement x="130" y="2" width="30" height="10"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.Float"><![CDATA[$V{PriceSum}]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</columnFooter>
|
||||
<pageFooter>
|
||||
<band height="14">
|
||||
<rectangle>
|
||||
<reportElement mode="Transparent" x="0" y="2" width="325" height="10" forecolor="#808000"/>
|
||||
<graphicElement pen="Thin"/>
|
||||
</rectangle>
|
||||
<staticText>
|
||||
<reportElement x="0" y="2" width="60" height="10" forecolor="#808000"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Italic"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Page Footer]]></text>
|
||||
</staticText>
|
||||
<textField>
|
||||
<reportElement x="150" y="2" width="100" height="10" forecolor="#808000"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Italic"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA["Page " + String.valueOf($V{PAGE_NUMBER}) + " of "]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField evaluationTime="Report">
|
||||
<reportElement x="250" y="2" width="50" height="10" forecolor="#808000"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Italic"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.Integer"><![CDATA[$V{PAGE_NUMBER}]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</pageFooter>
|
||||
<summary>
|
||||
<band height="14">
|
||||
<rectangle>
|
||||
<reportElement x="0" y="2" width="325" height="10" forecolor="#808000" backcolor="#808000"/>
|
||||
<graphicElement pen="Thin"/>
|
||||
</rectangle>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="0" y="2" width="230" height="10" backcolor="#808000"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Italic"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Summary]]></text>
|
||||
</staticText>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="230" y="2" width="55" height="10" backcolor="#808000"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Count :]]></text>
|
||||
</staticText>
|
||||
<textField pattern="#0">
|
||||
<reportElement mode="Opaque" x="285" y="2" width="40" height="10" backcolor="#808000"/>
|
||||
<textElement textAlignment="Right">
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.Integer"><![CDATA[$V{ProductCount}]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</summary>
|
||||
</jasperReport>
|
||||
Binary file not shown.
@@ -1,103 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Created using JasperAssistant (http://www.jasperassistant.com) -->
|
||||
<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
|
||||
|
||||
<jasperReport name="MasterReport" pageWidth="595" pageHeight="842" columnWidth="515" leftMargin="40" rightMargin="40" topMargin="50" bottomMargin="50">
|
||||
<reportFont name="Arial_Normal" isDefault="true" fontName="Arial" size="12" isBold="false" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<reportFont name="Arial_Bold" isDefault="false" fontName="Arial" size="12" isBold="true" isItalic="false" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica-Bold" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<reportFont name="Arial_Italic" isDefault="false" fontName="Arial" size="12" isBold="false" isItalic="true" isUnderline="false" isStrikeThrough="false" pdfFontName="Helvetica-Oblique" pdfEncoding="Cp1252" isPdfEmbedded="false"/>
|
||||
<parameter name="ProductsSubReport" class="net.sf.jasperreports.engine.JasperReport"/>
|
||||
<parameter name="SubReportData" class="net.sf.jasperreports.engine.JRDataSource"/>
|
||||
<field name="city" class="java.lang.String">
|
||||
</field>
|
||||
<title>
|
||||
<band height="50">
|
||||
<line>
|
||||
<reportElement x="0" y="0" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
<staticText>
|
||||
<reportElement x="0" y="10" width="515" height="30"/>
|
||||
<textElement textAlignment="Center">
|
||||
<font reportFont="Arial_Normal" size="22"/>
|
||||
</textElement>
|
||||
<text><![CDATA[Master Report]]></text>
|
||||
</staticText>
|
||||
</band>
|
||||
</title>
|
||||
<pageHeader>
|
||||
<band height="21">
|
||||
<rectangle>
|
||||
<reportElement x="0" y="5" width="515" height="15" backcolor="#333333"/>
|
||||
<graphicElement pen="None"/>
|
||||
</rectangle>
|
||||
<staticText>
|
||||
<reportElement mode="Opaque" x="0" y="5" width="515" height="15" forecolor="#ffffff" backcolor="#333333"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[City List]]></text>
|
||||
</staticText>
|
||||
<line>
|
||||
<reportElement x="0" y="20" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
</band>
|
||||
</pageHeader>
|
||||
<detail>
|
||||
<band height="50">
|
||||
<textField>
|
||||
<reportElement x="5" y="5" width="100" height="15" isPrintWhenDetailOverflows="true"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[$F{city}]]></textFieldExpression>
|
||||
</textField>
|
||||
<staticText>
|
||||
<reportElement isPrintRepeatedValues="false" x="110" y="5" width="100" height="15" isPrintWhenDetailOverflows="true"/>
|
||||
<textElement>
|
||||
<font reportFont="Arial_Bold"/>
|
||||
</textElement>
|
||||
<text><![CDATA[(continued)]]></text>
|
||||
</staticText>
|
||||
<line>
|
||||
<reportElement x="0" y="20" width="515" height="1" isPrintWhenDetailOverflows="true"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
<subreport>
|
||||
<reportElement isPrintRepeatedValues="false" x="5" y="25" width="325" height="20" isRemoveLineWhenBlank="true" backcolor="#ffcc99"/>
|
||||
<subreportParameter name="City">
|
||||
<subreportParameterExpression><![CDATA[$F{city}]]></subreportParameterExpression>
|
||||
</subreportParameter>
|
||||
<dataSourceExpression><![CDATA[$P{SubReportData}]]></dataSourceExpression>
|
||||
<subreportExpression class="net.sf.jasperreports.engine.JasperReport"><![CDATA[$P{ProductsSubReport}]]></subreportExpression>
|
||||
</subreport>
|
||||
<!--<subreport>
|
||||
<reportElement positionType="Float" x="335" y="25" width="175" height="20" isRemoveLineWhenBlank="true" backcolor="#99ccff"/>
|
||||
<subreportParameter name="City">
|
||||
<subreportParameterExpression><![CDATA[$F{City}]]></subreportParameterExpression>
|
||||
</subreportParameter>
|
||||
<connectionExpression><![CDATA[$P{REPORT_CONNECTION}]]></connectionExpression>
|
||||
<subreportExpression class="java.lang.String"><![CDATA["AddressReport.jasper"]]></subreportExpression>
|
||||
</subreport> -->
|
||||
</band>
|
||||
</detail>
|
||||
<pageFooter>
|
||||
<band height="40">
|
||||
<line>
|
||||
<reportElement x="0" y="10" width="515" height="1"/>
|
||||
<graphicElement/>
|
||||
</line>
|
||||
<textField>
|
||||
<reportElement x="200" y="20" width="80" height="15"/>
|
||||
<textElement textAlignment="Right"/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA["Page " + String.valueOf($V{PAGE_NUMBER}) + " of"]]></textFieldExpression>
|
||||
</textField>
|
||||
<textField evaluationTime="Report">
|
||||
<reportElement x="280" y="20" width="75" height="15"/>
|
||||
<textElement/>
|
||||
<textFieldExpression class="java.lang.String"><![CDATA[" " + String.valueOf($V{PAGE_NUMBER})]]></textFieldExpression>
|
||||
</textField>
|
||||
</band>
|
||||
</pageFooter>
|
||||
</jasperReport>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user