View Javadoc

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     // Static -------------------------------------------------------------------
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     // Attributes ---------------------------------------------------------------
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     // Public -------------------------------------------------------------------
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       // Try instantiating properties extractor now.
105       instantiatePropertiesExtractor();
106    }
107 
108 
109    // Implementation of JMSConsumerBean ----------------------------------------
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       // Process only the ObjectMessage.
122       if (message instanceof ObjectMessage){
123          // Retrieve object from message.
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          // Prepare & extract message properties.
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             // Launching notifier (log start time).
145             if (log.isInfoEnabled())
146                log.info("Launching the notifier '" + name + "' JMS adapter. Start time is: " + new Date());
147             // Retrieve mailing list corresponding to event.
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             // Retrieve mailing list subscribers and notity them !
159             RemoteSubscriber[] subscribers = retrieveSubscribers(list);
160             getSubscribersNotifier().notify(subscribers, title, content);
161          }
162          catch (NotificationException ne){
163             // Log exception message.
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             // Log exception message.
170             log.error("Throwable caught when running " + name + " notifier.");
171             log.error("Here's the detailed message: " + t.getMessage());
172          }
173          finally{
174             // Log end date.
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    // Implementation of Configurable -------------------------------------------
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       // Use script launcher parameters as base.
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          // Delegate to super class first.
217          super.setParameter(parameterName, parameterValue);
218       }
219       catch (InvalidParameterException ipe){
220          // Then test the managed parameters.
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    // Private ------------------------------------------------------------------
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             // Load class from context class loader and retrieve new instance.
245             Class clazz = Thread.currentThread().getContextClassLoader().loadClass(extractorClass);
246             extractor = (PropertiesExtractor)clazz.newInstance();
247          }
248          catch (Exception e){
249             // Log and wrap within an InvalidParameterException.
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 }