001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.mail;
018    
019    import java.util.Collection;
020    import java.util.Iterator;
021    import java.util.Properties;
022    
023    import javax.mail.Authenticator;
024    import javax.mail.Session;
025    import javax.naming.Context;
026    import javax.naming.InitialContext;
027    import javax.naming.Name;
028    import javax.naming.NamingException;
029    
030    import org.apache.commons.logging.Log;
031    import org.apache.commons.logging.LogFactory;
032    import org.apache.geronimo.gbean.GBeanInfo;
033    import org.apache.geronimo.gbean.GBeanInfoBuilder;
034    import org.apache.geronimo.gbean.GBeanLifecycle;
035    import org.apache.geronimo.naming.ResourceSource;
036    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
037    import org.apache.geronimo.management.JavaMailResource;
038    
039    
040    /**
041     * GBean that provides access to JavaMail Sessions.
042     * <p/>
043     * This GBean is used to generate JavaMail Sessions.  JavaMail properties that
044     * are common to all JavaMail Sessions are provided via member variables of this
045     * class.
046     *
047     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
048     * @see ProtocolGBean
049     * @see SMTPTransportGBean
050     * @see POP3StoreGBean
051     * @see IMAPStoreGBean
052     */
053    public class MailGBean implements GBeanLifecycle, JavaMailResource, ResourceSource {
054    
055        private final Log log = LogFactory.getLog(MailGBean.class);
056    
057        private final String objectName;
058        private final Collection protocols;
059        private Boolean useDefault;
060        private Properties properties;
061        private Authenticator authenticator;
062        private String storeProtocol;
063        private String transportProtocol;
064        private String host;
065        private String user;
066        private Boolean debug;
067        private String jndiName;
068    
069    
070        /**
071         * Construct an instance of MailGBean
072         * <p/>
073         * Values that are set in the individual member variables will override any of
074         * the corresponding values that have been set in the properties set.
075         *
076         * @param protocols         the set of protocol GBeans that contain protocol specific configurations
077         * @param useDefault        whether this GBean will return default Sessions or not
078         * @param properties        the set of default properties for the protocols
079         * @param authenticator     the authenticator object
080         * @param storeProtocol     the store protocol that Sessions created from this GBean will return
081         * @param transportProtocol the transport protocol that Sessions created from this GBean will return
082         * @param host              the default Mail server
083         * @param user              the username to provide when connecting to a Mail server
084         * @param debug             the debug setting for Sessions created from this GBean
085         * @param jndiName          the JNDI name to which the mail Session should be bound
086         */
087        public MailGBean(String objectName, Collection protocols, Boolean useDefault, Properties properties, Authenticator authenticator,
088                         String storeProtocol, String transportProtocol, String host, String user, Boolean debug, String jndiName) {
089            this.objectName = objectName;
090            this.protocols = protocols;
091            setUseDefault(useDefault);
092            this.properties = (properties == null ? new Properties() : properties);
093            setAuthenticator(authenticator);
094            setStoreProtocol(storeProtocol);
095            setTransportProtocol(transportProtocol);
096            setHost(host);
097            setUser(user);
098            setDebug(debug);
099            setJndiName(jndiName);
100        }
101    
102        /**
103         * Returns the set of protocol GBeans that contain protocol specific configurations.
104         */
105        public Collection getProtocols() {
106            return protocols;
107        }
108    
109        /**
110         * Returns whether this GBean will return default Sessions or not.
111         */
112        public Boolean getUseDefault() {
113            return useDefault;
114        }
115    
116        /**
117         * Sets whether this GBean will return default Sessions or not,
118         *
119         * @param useDefault whether this GBean will return default Sessions or not
120         */
121        public void setUseDefault(Boolean useDefault) {
122            this.useDefault = useDefault;
123        }
124    
125        /**
126         * Returns the set of default properties for the protocols.
127         * <p/>
128         * Note: Proerties that are set here will override the properties that are
129         * set in the protocol GBeans.
130         */
131        public Properties getProperties() {
132            return properties;
133        }
134    
135        /**
136         * Sets the set of default properties for the protocols.
137         * <p/>
138         * Note: Proerties that are set here will override the properties that are
139         * set in the protocol GBeans.
140         *
141         * @param properties the set of default properties for the protocols
142         */
143        public void setProperties(Properties properties) {
144            this.properties = properties;
145        }
146    
147        /**
148         * Returns the authenticator object.
149         * <p/>
150         * Used only if a new Session object is created. Otherwise, it must match
151         * the Authenticator used to create the Session.
152         */
153        public Authenticator getAuthenticator() {
154            return authenticator;
155        }
156    
157        /**
158         * Sets the authenticator object.
159         * <p/>
160         * Used only if a new Session object is created. Otherwise, it must match
161         * the Authenticator used to create the Session.
162         *
163         * @param authenticator the authenticator object
164         */
165        public void setAuthenticator(Authenticator authenticator) {
166            this.authenticator = authenticator;
167        }
168    
169        /**
170         * Returns the store protocol that Sessions created from this GBean will return.
171         * <p/>
172         * Specifies the default Message Access Protocol. The Session.getStore()
173         * method returns a Store object that implements this protocol. The client
174         * can override this property and explicitly specify the protocol with the
175         * Session.getStore(String protocol) method.
176         */
177        public String getStoreProtocol() {
178            return storeProtocol;
179        }
180    
181        /**
182         * Sets the store protocol that Sessions created from this GBean will return.
183         * <p/>
184         * Specifies the default Message Access Protocol. The Session.getStore()
185         * method returns a Store object that implements this protocol. The client
186         * can override this property and explicitly specify the protocol with the
187         * Session.getStore(String protocol) method.
188         * <p/>
189         * Values that are set here will override any of the corresponding value
190         * that has been set in the properties.
191         *
192         * @param storeProtocol the store protocol that Sessions created from this GBean will return
193         */
194        public void setStoreProtocol(String storeProtocol) {
195            this.storeProtocol = storeProtocol;
196        }
197    
198        /**
199         * Returns the transport protocol that Sessions created from this GBean will return.
200         * <p/>
201         * Specifies the default Transport Protocol. The Session.getTransport()
202         * method returns a Transport object that implements this protocol. The
203         * client can override this property and explicitly specify the protocol
204         * by using Session.getTransport(String protocol) method.
205         */
206        public String getTransportProtocol() {
207            return transportProtocol;
208        }
209    
210        /**
211         * Sets the transport protocol that Sessions created from this GBean will return.
212         * <p/>
213         * Specifies the default Transport Protocol. The Session.getTransport()
214         * method returns a Transport object that implements this protocol. The
215         * client can override this property and explicitly specify the protocol
216         * by using Session.getTransport(String protocol) method.
217         * <p/>
218         * Values that are set here will override any of the corresponding value
219         * that has been set in the properties.
220         *
221         * @param transportProtocol the transport protocol that Sessions created from this GBean will return
222         */
223        public void setTransportProtocol(String transportProtocol) {
224            this.transportProtocol = transportProtocol;
225        }
226    
227        /**
228         * Returns the default Mail server.
229         * <p/>
230         * Specifies the default Mail server. The Store and Transport object’s
231         * connect methods use this property, if the protocolspecific host property
232         * is absent, to locate the target host.
233         */
234        public String getHost() {
235            return host;
236        }
237    
238        /**
239         * Sets the default Mail server.
240         * <p/>
241         * Specifies the default Mail server. The Store and Transport object’s
242         * connect methods use this property, if the protocolspecific host property
243         * is absent, to locate the target host.
244         * <p/>
245         * Values that are set here will override any of the corresponding value
246         * that has been set in the properties.
247         *
248         * @param host the default Mail server
249         */
250        public void setHost(String host) {
251            this.host = host;
252        }
253    
254        /**
255         * Returns the username to provide when connecting to a Mail server.
256         * <p/>
257         * Specifies the username to provide when connecting to a Mail server. The
258         * Store and Transport object’s connect methods use this property, if the
259         * protocolspecific username property is absent, to obtain the username.
260         */
261        public String getUser() {
262            return user;
263        }
264    
265        /**
266         * Sets the username to provide when connecting to a Mail server.
267         * <p/>
268         * Specifies the username to provide when connecting to a Mail server. The
269         * Store and Transport object’s connect methods use this property, if the
270         * protocolspecific username property is absent, to obtain the username.
271         * <p/>
272         * Values that are set here will override any of the corresponding value
273         * that has been set in the properties.
274         *
275         * @param user the username to provide when connecting to a Mail server
276         */
277        public void setUser(String user) {
278            this.user = user;
279        }
280    
281        /**
282         * Returns the debug setting for Sessions created from this GBean.
283         */
284        public Boolean getDebug() {
285            return debug;
286        }
287    
288        /**
289         * Sets the debug setting for Sessions created from this GBean.
290         * <p/>
291         * Values that are set here will override any of the corresponding value
292         * that has been set in the properties.
293         *
294         * @param debug the debug setting for Sessions created from this GBean
295         */
296        public void setDebug(Boolean debug) {
297            this.debug = debug;
298        }
299    
300        /**
301         * Gets the JNDI name to which the mail Session should be bound
302         * @return the JNDI name to which the mail Session should be bound
303         */
304        public String getJndiName() {
305            return jndiName;
306        }
307    
308        /**
309         * Sets the JNDI name to which the mail Session should be bound
310         * @param jndiName the JNDI name to which the mail Session should be bound
311         */
312        public void setJndiName(String jndiName) {
313            this.jndiName = jndiName;
314        }
315    
316        public Object $getResource() {
317            Properties props = new Properties(properties);
318    
319            if (protocols != null) {
320                for (Iterator iter = protocols.iterator(); iter.hasNext();) {
321                    ProtocolGBean protocol = (ProtocolGBean) iter.next();
322                    protocol.addOverrides(props);
323                }
324            }
325    
326            props.putAll(properties);
327    
328            if (storeProtocol != null) props.put("mail.store.protocol", storeProtocol);
329            if (transportProtocol != null) props.put("mail.transport.protocol", transportProtocol);
330            if (host != null) props.put("mail.host", host);
331            if (user != null) props.put("mail.user", user);
332            // this needs to be translated into a string version.
333            if (debug != null) props.put("mail.debug", debug.toString());
334    
335            if (Boolean.TRUE.equals(useDefault)) {
336                if (authenticator == null) {
337                    return Session.getDefaultInstance(props);
338                } else {
339                    return Session.getDefaultInstance(props, authenticator);
340                }
341            } else {
342                if (authenticator == null) {
343                    return Session.getInstance(props);
344                } else {
345                    return Session.getInstance(props, authenticator);
346                }
347            }
348        }
349    
350        public void doStart() throws Exception {
351            log.debug("Started " + objectName + " - will return "
352                     + (Boolean.TRUE.equals(useDefault) ? "default" : "new")
353                     + " JavaMail Session "
354                     + (authenticator == null ? "without" : "with")
355                     + " authenticator");
356    
357            String jndiName = getJndiName();
358            if (jndiName != null && jndiName.length() > 0) {
359                // first get the resource incase there are exceptions
360                Object value = $getResource();
361    
362                // get the initial context
363                Context context = new InitialContext();
364                Name parsedName = context.getNameParser("").parse(jndiName);
365    
366                // create intermediate contexts
367                for (int i = 1; i < parsedName.size(); i++) {
368                    Name contextName = parsedName.getPrefix(i);
369                    if (!bindingExists(context, contextName)) {
370                        context.createSubcontext(contextName);
371                    }
372                }
373    
374                // bind
375                context.bind(jndiName, value);
376                log.info("JavaMail session bound to " + jndiName);
377            }
378        }
379    
380        public void doStop() throws Exception {
381            log.debug("Stopped " + objectName);
382            stop();
383        }
384    
385        public void doFail() {
386            log.warn("Failed " + objectName);
387            stop();
388        }
389    
390        private void stop() {
391            String jndiName = getJndiName();
392            if (jndiName != null && jndiName.length() > 0) {
393                try {
394                    Context context = new InitialContext();
395                    context.unbind(jndiName);
396                    log.info("JavaMail session unbound from " + jndiName);
397                } catch (NamingException e) {
398                    // we tried... this is a common error which occurs during shutdown due to ordering
399                }
400            }
401        }
402    
403        private static boolean bindingExists(Context context, Name contextName) {
404            try {
405                return context.lookup(contextName) != null;
406            } catch (NamingException e) {
407            }
408            return false;
409        }
410    
411        /**
412         * Returns the GBean name of this Mail GBean
413         */
414        public String getObjectName() {
415            return objectName;
416        }
417    
418        public boolean isStateManageable() {
419            return false;
420        }
421    
422        public boolean isStatisticsProvider() {
423            return false;
424        }
425    
426        public boolean isEventProvider() {
427            return false;
428        }
429    
430        public static final GBeanInfo GBEAN_INFO;
431    
432        static {
433            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic(MailGBean.class, NameFactory.JAVA_MAIL_RESOURCE);
434    
435            infoFactory.addAttribute("objectName", String.class, false);
436            infoFactory.addReference("Protocols", ProtocolGBean.class, NameFactory.GERONIMO_SERVICE);
437            infoFactory.addAttribute("useDefault", Boolean.class, true);
438            infoFactory.addAttribute("properties", Properties.class, true);
439            infoFactory.addReference("Authenticator", Authenticator.class, NameFactory.GERONIMO_SERVICE);
440            infoFactory.addAttribute("storeProtocol", String.class, true);
441            infoFactory.addAttribute("transportProtocol", String.class, true);
442            infoFactory.addAttribute("host", String.class, true);
443            infoFactory.addAttribute("user", String.class, true);
444            infoFactory.addAttribute("debug", Boolean.class, true);
445            infoFactory.addAttribute("jndiName", String.class, true);
446            infoFactory.addOperation("$getResource");
447            infoFactory.addOperation("getProtocols");
448            infoFactory.addInterface(JavaMailResource.class);
449    
450            infoFactory.setConstructor(new String[]{"objectName",
451                                                    "Protocols",
452                                                    "useDefault",
453                                                    "properties",
454                                                    "Authenticator",
455                                                    "storeProtocol",
456                                                    "transportProtocol",
457                                                    "host",
458                                                    "user",
459                                                    "debug",
460                                                    "jndiName"});
461    
462            GBEAN_INFO = infoFactory.getBeanInfo();
463        }
464    
465        public static GBeanInfo getGBeanInfo() {
466            return GBEAN_INFO;
467        }
468    }