1 /**
2 * Copyright 2005-2006 the original author or authors.
3 *
4 * Licensed under the Gnu General Pubic License, Version 2.0 (the
5 * "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/gpl-license.php
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the Gnu General Public License for more details.
14 */
15 package org.figure8.join.services.notification;
16
17 import org.figure8.join.core.ParameterDefinition;
18 import org.figure8.join.core.InvalidParameterException;
19 import org.figure8.join.core.messaging.JMSConsumerBean;
20 import org.figure8.join.services.properties.PropertiesExtractor;
21 import org.figure8.join.services.remoting.beans.RemoteMailingList;
22 import org.figure8.join.services.remoting.beans.RemoteSubscriber;
23 import org.figure8.join.util.LogUtil;
24 import org.figure8.join.util.AntHelper;
25
26 import org.apache.commons.logging.Log;
27
28 import javax.jms.Message;
29 import javax.jms.ObjectMessage;
30
31 import java.util.Date;
32 import java.util.List;
33 import java.util.ArrayList;
34 import java.util.Properties;
35 /**
36 * This is a base class for JMS consumers that wants to notify subscribers
37 * when a new message arrive. It acts as an adapter between the JMS world
38 * and the {@link SubscribersNotifier} facilities.
39 * <br/>
40 * This class is a JMSConsumerBean that is {@link org.figure8.join.core.Configurable} :
41 * it defines a mandatory parameter that should be a {@link PropertiesExtractor} impl.
42 * This extractor help the transition from a JMS view (that is object message
43 * based) to a notification content view (a template with curly braced varaibles representing
44 * object properties).
45 * <br/>
46 * Extensions of this class should specifiy which SubscribersNotifier implementation
47 * to use when running scripts.
48 *
49 * @see org.figure8.join.core.Configurable
50 * @see org.figure8.join.core.messaging.JMSConsumerBean
51 * @see org.figure8.join.services.properties.PropertiesExtractor
52 *
53 * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
54 * @version $Revision: 1.1 $
55 */
56 public abstract class SubscribersNotifierJMSAdapter extends SubscribersNotifierAdapterSupport implements JMSConsumerBean{
57
58
59
60 /** Get a commons logger. */
61 private static final Log log = LogUtil.getLog(SubscribersNotifierJMSAdapter.class);
62
63 /** Name of configurable parameter denoting the object property representing the key of resource to notify event for */
64 public static final String RESOURCE_PROP_PARAM = "resourceKeyProperty";
65 /** Name of configurable parameter denoting the propertiesExtractor to use for getting props. */
66 public static final String EXTRACTOR_PARAM = "propertiesExtractor";
67
68 /** List of {@link ParameterDefinition}s supported by this adapter */
69 protected static List parameters = new ArrayList();
70
71 /** ParameterDefinition representation of parameter denoting the key of resource to notify event for. */
72 protected static final ParameterDefinition resourcePropParam = new ParameterDefinition(RESOURCE_PROP_PARAM,
73 "The object property representing the key of resource to notify event for", "deliverable.type.key", false);
74 /** ParameterDefinition representation of parameter denoting the propertiesExtractor to use. */
75 protected static final ParameterDefinition extractionParam = new ParameterDefinition(EXTRACTOR_PARAM,
76 "The PropertiesExtractor implementation to use", "org.figure8.join.services.properties.DefaultPropertiesExtractor", true);
77
78
79
80
81
82 /** The property representing resource to notify event for. */
83 private String resourceProperty;
84 /** The class name properties extractor implementation to use. */
85 private String extractorClass;
86 /** The PropertiesExtractor implementation to use. */
87 private PropertiesExtractor extractor = null;
88
89
90
91
92 /** @param resourceProperty Property representing resource */
93 public void setResourceProperty(String resourceProperty){
94 this.resourceProperty = resourceProperty;
95 }
96 /**
97 * Specify the class of PropertiesExtractor implementation to use
98 * @param extractorClass The FQN of Java class representing PropertiesExtractor impl
99 * @throws org.figure8.join.core.InvalidParameterException if <b>extractorClass</b> is not found or does
100 * not implement the PropertiesExtractor interface
101 */
102 public void setPropertiesExtractorClass(String extractorClass) throws InvalidParameterException{
103 this.extractorClass = extractorClass;
104
105 instantiatePropertiesExtractor();
106 }
107
108
109
110
111 /**
112 * This method is executed on message delivery. This implementation only
113 * process message of type ObjectMessage. The process is the following :<br/>
114 * first: retrieve object from message and eventually extract its properties,<br/>
115 * @param message
116 */
117 public void onMessage(Message message){
118 if (log.isInfoEnabled())
119 log.info("Message received by SubscribersNotifierJMSAdapter '" + name + "'");
120
121
122 if (message instanceof ObjectMessage){
123
124 Object obj = null;
125 try {obj = ((ObjectMessage)message).getObject();}
126 catch (Exception e){
127 log.fatal("Exception while extracting object from JMSMessage into '" + getName() + "'");
128 log.fatal("Here's the detailed message: " + e.getMessage());
129 return;
130 }
131
132 Properties props = new Properties();
133 if (extractor != null){
134 try {props.putAll(extractor.extract(obj));}
135 catch (InvalidParameterException ipe){
136 log.error("Exception while extracting properties from object into '" + getName() + "'");
137 log.error("Here's the detailed message: " + ipe.getMessage());
138 }
139 if (log.isDebugEnabled())
140 log.debug("Creating message with properties: " + props);
141 }
142
143 try{
144
145 if (log.isInfoEnabled())
146 log.info("Launching the notifier '" + name + "' JMS adapter. Start time is: " + new Date());
147
148 String resourceId = props.getProperty(resourceProperty);
149 RemoteMailingList list = retrieveMailingList(getEventKey(), resourceId);
150 if (log.isInfoEnabled())
151 log.info(name + " is notifying the subscribers of list '" + list.getName() + "'");
152 String title = AntHelper.replaceProperties(list.getMsgTitleTemplate(), props);
153 String content = AntHelper.replaceProperties(list.getMsgContentTemplate(), props);
154 if (log.isDebugEnabled()){
155 log.debug("Notifying with title: " + title);
156 log.debug("Notifying with content: " + content);
157 }
158
159 RemoteSubscriber[] subscribers = retrieveSubscribers(list);
160 getSubscribersNotifier().notify(subscribers, title, content);
161 }
162 catch (NotificationException ne){
163
164 log.error("NotificationException when running " + name + " notifier: " + ne.getMessage());
165 if (ne.getCause() != null)
166 log.error("Here's the root exception message: " + ne.getCause().getMessage());
167 }
168 catch (Throwable t){
169
170 log.error("Throwable caught when running " + name + " notifier.");
171 log.error("Here's the detailed message: " + t.getMessage());
172 }
173 finally{
174
175 if (log.isInfoEnabled())
176 log.info("Notifier '" + name + "' JMS adapter has end. Stop time is: " + new Date());
177 }
178 }
179 return;
180 }
181
182 /**
183 * Implement this method to stop current process and free resources.
184 * This method should not throw exceptions.
185 */
186 public void stop(){
187 log.info("Stopping the SubscribersNotifierJMSAdapter named '" + name + "'...");
188 }
189
190
191
192
193 /**
194 * Get this object parameters definitions as a list
195 * @return A list of {@code ParameterDefinition} objects
196 */
197 public List getParameterDefinitionsAsList(){
198
199 if (parameters.isEmpty()){
200 parameters.addAll(getSubscribersNotifier().getParameterDefinitionsAsList());
201 parameters.add(eventParam);
202 parameters.add(resourceParam);
203 parameters.add(extractionParam);
204 }
205 return parameters;
206 }
207
208 /**
209 * Set the value of a parameter using its name
210 * @param parameterName The name of parameter so set value for
211 * @param parameterValue The value of the paramater
212 * @throws InvalidParameterException if this parameter is not supported by this object
213 */
214 public void setParameter(String parameterName, String parameterValue) throws InvalidParameterException{
215 try{
216
217 super.setParameter(parameterName, parameterValue);
218 }
219 catch (InvalidParameterException ipe){
220
221 if (EVENT_PARAM.equals(parameterName))
222 setEventKey(parameterValue);
223 else if (RESOURCE_PROP_PARAM.equals(parameterName))
224 setResourceProperty(parameterValue);
225 else if (EXTRACTOR_PARAM.equals(parameterName))
226 setPropertiesExtractorClass(parameterValue);
227 else{
228 log.error("The parameter '" + parameterName + "' is not supported by SubscribersNotifierJMSAdapter");
229 throw new InvalidParameterException("The parameter '" + parameterName + "' is not supported by ScriptLauncherJMSAdapter");
230 }
231 }
232 }
233
234
235
236
237 /**
238 * Try to instantiate the <code>PropertiesExtractor</code> associated with this adapter
239 * @throws InvalidParameterException if the <b>extractorClass</b> inner field is not valid
240 */
241 private void instantiatePropertiesExtractor() throws InvalidParameterException{
242 if (extractorClass != null && extractor == null){
243 try{
244
245 Class clazz = Thread.currentThread().getContextClassLoader().loadClass(extractorClass);
246 extractor = (PropertiesExtractor)clazz.newInstance();
247 }
248 catch (Exception e){
249
250 log.error("Exception while trying to instantiate extractor for SubscribersNotifierJMSAdapter");
251 log.error("Here's the exception message : " + e.getMessage());
252 throw new InvalidParameterException("'" + extractorClass +
253 "' cannot be found or does not implement PropertiesExtractor", e);
254 }
255 }
256 }
257 }