KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > publishers > SametimeAnnouncementPublisher


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2003, ThoughtWorks, Inc.
4  * 651 W Washington Ave. Suite 600
5  * Chicago, IL 60661 USA
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * + Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * + Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  *
20  * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
21  * names of its contributors may be used to endorse or promote
22  * products derived from this software without specific prior
23  * written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  ********************************************************************************/

37 package net.sourceforge.cruisecontrol.publishers;
38
39 import java.util.Arrays JavaDoc;
40 import java.util.HashSet JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.Set JavaDoc;
43 import java.util.StringTokenizer JavaDoc;
44
45 import net.sourceforge.cruisecontrol.CruiseControlException;
46 import net.sourceforge.cruisecontrol.util.ValidationHelper;
47 import net.sourceforge.cruisecontrol.util.XMLLogHelper;
48
49 import org.apache.log4j.Logger;
50
51 import com.lotus.sametime.announcement.AnnouncementService;
52 import com.lotus.sametime.community.CommunityService;
53 import com.lotus.sametime.community.Login;
54 import com.lotus.sametime.community.LoginEvent;
55 import com.lotus.sametime.community.LoginListener;
56 import com.lotus.sametime.core.comparch.DuplicateObjectException;
57 import com.lotus.sametime.core.comparch.STSession;
58 import com.lotus.sametime.core.types.STGroup;
59 import com.lotus.sametime.core.types.STObject;
60 import com.lotus.sametime.core.types.STUser;
61 import com.lotus.sametime.lookup.GroupContentEvent;
62 import com.lotus.sametime.lookup.GroupContentGetter;
63 import com.lotus.sametime.lookup.GroupContentListener;
64 import com.lotus.sametime.lookup.LookupService;
65 import com.lotus.sametime.lookup.ResolveEvent;
66 import com.lotus.sametime.lookup.ResolveListener;
67 import com.lotus.sametime.lookup.Resolver;
68
69 /**
70  * Publish (simple) build results by sending a Sametime announcement.
71  * <p>Requires Sametime 3.0 Java Toolkit. See http://www-10.lotus.com/ldd/toolkits<br>
72  * In particular, requires STComm.jar
73  * @author Richard Lewis-Shell
74  */

75 public class SametimeAnnouncementPublisher extends LinkEmailPublisher
76              implements LoginListener, ResolveListener, GroupContentListener {
77     private static final Logger LOG = Logger.getLogger(SametimeAnnouncementPublisher.class);
78
79     public static final String JavaDoc RESOLVE_CONFLICTS_RECIPIENT = "recipient"; // default
80
public static final String JavaDoc RESOLVE_CONFLICTS_IGNORE = "ignore";
81     public static final String JavaDoc RESOLVE_CONFLICTS_WARN = "warn";
82     public static final String JavaDoc RESOLVE_CONFLICTS_ERROR = "error";
83     
84     public static final String JavaDoc RESOLVE_FAIL_IGNORE = "ignore";
85     public static final String JavaDoc RESOLVE_FAIL_WARN = "warn";
86     public static final String JavaDoc RESOLVE_FAIL_ERROR = "error"; // default
87

88     public static final String JavaDoc QUERY_GROUP_CONTENT_FAIL_IGNORE = "ignore";
89     public static final String JavaDoc QUERY_GROUP_CONTENT_FAIL_WARN = "warn";
90     public static final String JavaDoc QUERY_GROUP_CONTENT_FAIL_ERROR = "error"; // default
91

92     // Configurable properties
93

94     // Sametime community
95
private String JavaDoc community;
96     // whether to resolve addresses as users
97
private boolean resolveUsers = true;
98     // whether to resolve addresses as groups
99
private boolean resolveGroups = true;
100     // send to group contents, rather than the group itself
101
private boolean useGroupContent = true;
102     // valid values: recipient, ignore, fail
103
private String JavaDoc handleResolveConflicts = RESOLVE_CONFLICTS_RECIPIENT;
104     // how to handle resolve failures
105
private String JavaDoc handleResolveFails = RESOLVE_FAIL_ERROR;
106     // how to handle query group content failures
107
private String JavaDoc handleQueryGroupContentFails = QUERY_GROUP_CONTENT_FAIL_ERROR;
108     // how long to wait (in seconds) before giving up on an interaction with the sametime server
109
private int timeout = 10;
110     // how long to sleep (in milliseconds) before looking for a response from the sametime server
111
private int sleepMillis = 5;
112         
113     // Internal state
114

115     // Sametime components
116
private STSession session;
117     private CommunityService communityService;
118     private AnnouncementService announcementService;
119     private LookupService lookupService;
120
121     // list of users/groups to resolve into STObjects
122
private Set JavaDoc usernamesToResolveSet;
123     // login, null if not logged in
124
private Login login;
125     // list of resolved STUsers
126
private Set JavaDoc recipientUserSet = null;
127     // list of resolved STGroups
128
private Set JavaDoc recipientGroupSet = null;
129     // list of resovled user/group NAMES
130
private Set JavaDoc resolvedNameSet = null;
131     // used to temporarily hold group content (while getting)
132
private STObject[] groupContent = null;
133     // error messages constructed in a different thread, thrown as exceptions
134
// in the main thread
135
private String JavaDoc resolveFailMessage = null;
136     private String JavaDoc resolveConflictMessage = null;
137     private String JavaDoc queryGroupContentFailMessage = null;
138     
139     // rename the mailhost property
140
public String JavaDoc getHost() {
141         return this.getMailHost();
142     }
143     
144     // rename the mailhost property
145
public void setHost(String JavaDoc value) {
146         this.setMailHost(value);
147     }
148
149     public String JavaDoc getCommunity() {
150         return this.community;
151     }
152
153     public void setCommunity(String JavaDoc value) {
154         this.community = value;
155     }
156
157     public boolean isResolveUsers() {
158         return this.resolveUsers;
159     }
160     
161     public void setResolveUsers(boolean value) {
162         this.resolveUsers = value;
163     }
164     
165     public boolean isResolveGroups() {
166         return this.resolveGroups;
167     }
168     
169     public void setResolveGroups(boolean value) {
170         this.resolveGroups = value;
171     }
172     
173     public boolean isUseGroupContent() {
174         return this.useGroupContent;
175     }
176     
177     public void setUseGroupContent(boolean value) {
178         this.useGroupContent = value;
179     }
180     
181     public String JavaDoc getHandleQueryGroupContentFails() {
182         return this.handleQueryGroupContentFails;
183     }
184
185     public String JavaDoc getHandleResolveConflicts() {
186         return this.handleResolveConflicts;
187     }
188
189     public String JavaDoc getHandleResolveFails() {
190         return this.handleResolveFails;
191     }
192
193     public void setHandleQueryGroupContentFails(String JavaDoc string) {
194         this.handleQueryGroupContentFails = string;
195     }
196
197     public void setHandleResolveConflicts(String JavaDoc value) {
198         this.handleResolveConflicts = value;
199     }
200
201     public void setHandleResolveFails(String JavaDoc value) {
202         this.handleResolveFails = value;
203     }
204     
205     public int getTimeout() {
206         return this.timeout;
207     }
208
209     private int getTimeoutMillis() {
210         return this.getTimeout() * 1000;
211     }
212
213     public int getSleepMillis() {
214         return sleepMillis;
215     }
216
217     public void setTimeout(int value) {
218         timeout = value;
219     }
220
221     public void setSleepMillis(int value) {
222         sleepMillis = value;
223     }
224
225     // override this so we can otherwise rely on EmailPublisher's validation
226
// returnAddress makes no sense for Sametime
227
public String JavaDoc getReturnAddress() {
228         return "";
229     }
230
231     public void validate() throws CruiseControlException {
232         ValidationHelper.assertIsSet(getHost(), "host", this.getClass());
233         ValidationHelper.assertIsSet(getUsername(), "username", this.getClass());
234
235         ensureInEqualsIgnoreCase(this.getHandleResolveFails(), "handleResolveFails",
236             new String JavaDoc[] {RESOLVE_FAIL_ERROR, RESOLVE_FAIL_WARN, RESOLVE_FAIL_WARN});
237
238         ensureInEqualsIgnoreCase(this.getHandleResolveConflicts(), "handleResolveConflicts",
239             new String JavaDoc[] {RESOLVE_CONFLICTS_ERROR, RESOLVE_CONFLICTS_IGNORE,
240                           RESOLVE_CONFLICTS_RECIPIENT, RESOLVE_CONFLICTS_WARN});
241
242         ensureInEqualsIgnoreCase(this.getHandleQueryGroupContentFails(), "handleQueryGroupContentFails",
243             new String JavaDoc[] {QUERY_GROUP_CONTENT_FAIL_ERROR, QUERY_GROUP_CONTENT_FAIL_IGNORE,
244                           QUERY_GROUP_CONTENT_FAIL_WARN});
245     }
246
247     private static void ensureInEqualsIgnoreCase(String JavaDoc attribute, String JavaDoc attributeName,
248                                                  String JavaDoc[] strings) throws CruiseControlException {
249         for (int i = 0; i < strings.length; i++) {
250             String JavaDoc string = strings[i];
251             if (attribute.equalsIgnoreCase(string)) {
252                 return;
253             }
254         }
255         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("'");
256         buf.append(attributeName).append("' attribute invalid. - valid values are ");
257         for (int i = 0; i < strings.length; i++) {
258             if (i > 0) {
259                 buf.append(" | ");
260             }
261             buf.append(strings[i]);
262         }
263         ValidationHelper.fail(buf.toString());
264     }
265
266     // use the build results URL as the message content
267
protected String JavaDoc createMessage(XMLLogHelper logHelper) {
268         return this.getBuildResultsURL() == null ? null : super.createMessage(logHelper);
269     }
270
271     // not really sending mail, but LinkEmailPublisher has a lot of useful logic
272
// to reuse - skipUsers, spamWhileBroken etc...
273
protected boolean sendMail(String JavaDoc toList, String JavaDoc subject, String JavaDoc message, boolean important)
274         throws CruiseControlException {
275  
276         boolean announcementSent = false;
277
278         LOG.info("Sending sametime notifications.");
279
280         // turn the comma separated toList into a list of users/groups to be resolved
281
this.usernamesToResolveSet = new HashSet JavaDoc();
282         for (StringTokenizer JavaDoc strtok = new StringTokenizer JavaDoc(toList, ","); strtok.hasMoreTokens(); ) {
283             String JavaDoc token = strtok.nextToken();
284             this.usernamesToResolveSet.add(token.trim());
285         }
286     
287         try {
288             this.session = new STSession("CruiseControl build notification" + this);
289         } catch (DuplicateObjectException ex) {
290             throw new RuntimeException JavaDoc("cannot create sametime session" + ex);
291         }
292         this.session.loadSemanticComponents();
293         this.session.start();
294
295         this.communityService = (CommunityService) session.getCompApi(CommunityService.COMP_NAME);
296         this.lookupService = (LookupService) session.getCompApi(LookupService.COMP_NAME);
297         this.announcementService = (AnnouncementService) session.getCompApi(AnnouncementService.COMP_NAME);
298         this.communityService.addLoginListener(this);
299         if (this.getPassword() != null) {
300             if (LOG.isDebugEnabled()) {
301                 LOG.debug("loginByPassword(" + this.getHost() + ", " + this.getUsername() + ", ****, "
302                     + this.getCommunity() + ")");
303             }
304             this.communityService.loginByPassword(this.getHost(), this.getUsername(), this.getPassword(),
305                                                   this.getCommunity());
306         } else {
307             if (LOG.isDebugEnabled()) {
308                 LOG.debug("loginAsAnon(" + this.getHost() + ", " + this.getUsername()
309                     + this.getCommunity() + ")");
310             }
311             this.communityService.loginAsAnon(this.getHost(), this.getUsername(), this.getCommunity()); // not tested
312
}
313
314         // if we lose the connection, just give up
315
this.communityService.disableAutomaticReconnect();
316
317         boolean bored = false;
318         long waitStart = System.currentTimeMillis();
319         while (!this.isLoggedIn() && !bored) {
320             try {
321                 Thread.sleep(this.getSleepMillis());
322             } catch (InterruptedException JavaDoc ex) {
323                 throw new RuntimeException JavaDoc("sleep interrupted: " + ex);
324             }
325             bored = System.currentTimeMillis() - waitStart > this.getTimeoutMillis();
326         }
327         if (bored && !this.isLoggedIn()) {
328             throw new RuntimeException JavaDoc("bored waiting for login");
329         }
330     
331         try {
332             this.resolve();
333
334             if (this.isUseGroupContent()) {
335                 this.getGroupContent();
336             } else if (this.recipientGroupSet != null) {
337                 this.recipientUserSet.addAll(this.recipientGroupSet);
338             }
339
340             if (LOG.isDebugEnabled()) {
341                 LOG.debug("announce to: " + this.recipientUserSet);
342             }
343             // "\r\n" appears to be the end of line marker sametime uses
344
String JavaDoc announcementMessage = message == null ? subject : subject + "\r\n" + message;
345             this.announcementService.sendAnnouncement((STObject[]) this.recipientUserSet.toArray(
346                                       new STObject[this.recipientUserSet.size()]), false, announcementMessage);
347             announcementSent = true;
348         } finally {
349             try {
350                 this.communityService.logout();
351             } finally {
352                 try {
353                     this.session.stop();
354                 } finally {
355                     this.session.unloadSession();
356                 }
357             }
358             return announcementSent;
359         }
360     }
361
362     private void resolve() throws CruiseControlException {
363         Resolver resolver = lookupService.createResolver(false, false, this.isResolveUsers(), this.isResolveGroups());
364         resolver.addResolveListener(this);
365         
366         try {
367             this.recipientUserSet = new HashSet JavaDoc();
368             this.recipientGroupSet = new HashSet JavaDoc();
369             this.resolvedNameSet = new HashSet JavaDoc();
370         
371             if (LOG.isDebugEnabled()) {
372                 LOG.debug("resolving: " + this.usernamesToResolveSet);
373             }
374             resolver.resolve(
375                (String JavaDoc[]) this.usernamesToResolveSet.toArray(new String JavaDoc[this.usernamesToResolveSet.size()]));
376         
377             // how long do we wait to resolve?
378
boolean bored = false;
379             long waitStart = System.currentTimeMillis();
380             while (!this.isResolvedAllUsersAndGroups() && !bored && !this.isResolveError()) {
381                 try {
382                     Thread.sleep(this.getSleepMillis());
383                 } catch (InterruptedException JavaDoc ex) {
384                     throw new RuntimeException JavaDoc("sleep interrupted: " + ex);
385                 }
386                 bored = System.currentTimeMillis() - waitStart > this.getTimeoutMillis();
387             }
388             if (this.resolveFailMessage != null
389                 && RESOLVE_FAIL_ERROR.equalsIgnoreCase(this.getHandleResolveFails())) {
390                 throw new CruiseControlException(this.resolveFailMessage);
391             }
392             if (this.resolveConflictMessage != null
393                 && RESOLVE_CONFLICTS_ERROR.equalsIgnoreCase(this.getHandleResolveConflicts())) {
394                 throw new CruiseControlException(this.resolveConflictMessage);
395             }
396             if (bored && !this.isResolvedAllUsersAndGroups()) {
397                 throw new CruiseControlException("bored waiting for user/group resolving");
398             }
399         } finally {
400             resolver.removeResolveListener(this);
401         }
402     }
403     
404     private boolean haveGroupContent() {
405         return this.groupContent != null;
406     }
407     
408     // convert the recipientGroupList into group content users
409
private void getGroupContent() throws CruiseControlException {
410         if (this.recipientGroupSet == null) {
411             return;
412         }
413         GroupContentGetter groupContentGetter = this.lookupService.createGroupContentGetter();
414         groupContentGetter.addGroupContentListener(this);
415         try {
416             for (Iterator JavaDoc i = this.recipientGroupSet.iterator(); i.hasNext(); ) {
417                 this.groupContent = null;
418                 STGroup group = (STGroup) i.next();
419                 groupContentGetter.queryGroupContent(group);
420                 // how long do we wait to resolve?
421
boolean bored = false;
422                 long waitStart = System.currentTimeMillis();
423                 while (!this.haveGroupContent() && !bored && !this.isQueryGroupContentError()) {
424                     try {
425                         Thread.sleep(this.getSleepMillis());
426                     } catch (InterruptedException JavaDoc ex) {
427                         throw new CruiseControlException("sleep interrupted: " + ex);
428                     }
429                     bored = System.currentTimeMillis() - waitStart > this.getTimeoutMillis();
430                 }
431                 if (this.isQueryGroupContentError()) {
432                     throw new CruiseControlException(this.queryGroupContentFailMessage);
433                 }
434                 if (bored && !this.haveGroupContent()) {
435                     throw new CruiseControlException("bored waiting to get group content for " + group);
436                 }
437             }
438         } finally {
439             groupContentGetter.removeGroupContentListener(this);
440         }
441     }
442     
443     private synchronized boolean isQueryGroupContentError() {
444         return this.queryGroupContentFailMessage != null
445                && QUERY_GROUP_CONTENT_FAIL_ERROR.equalsIgnoreCase(this.getHandleQueryGroupContentFails());
446     }
447     
448     private synchronized boolean isLoggedIn() {
449         return this.communityService != null && this.communityService.isLoggedIn() && this.login != null;
450     }
451     
452     // return true once all the users/groups to be resolved have been resolved
453
private synchronized boolean isResolvedAllUsersAndGroups() {
454         if (this.usernamesToResolveSet == null) {
455             return true;
456         }
457         for (Iterator JavaDoc i = this.usernamesToResolveSet.iterator(); i.hasNext(); ) {
458             String JavaDoc username = (String JavaDoc) i.next();
459             if (!this.resolvedNameSet.contains(username.toLowerCase())) {
460                 return false;
461             }
462         }
463         return true;
464     }
465     
466     private synchronized boolean isResolveError() {
467         return this.resolveFailMessage != null
468                && RESOLVE_FAIL_ERROR.equalsIgnoreCase(this.getHandleResolveFails())
469                || this.resolveConflictMessage != null
470                && RESOLVE_CONFLICTS_ERROR.equalsIgnoreCase(this.getHandleResolveConflicts());
471     }
472         
473     public synchronized void loggedIn(LoginEvent loginEvent) {
474         this.login = loginEvent.getLogin();
475     }
476
477     public synchronized void loggedOut(LoginEvent arg0) {
478         this.login = null;
479     }
480
481     public synchronized void resolveConflict(ResolveEvent resolveEvent) {
482         STObject[] resolvedList = resolveEvent.getResolvedList();
483         if (LOG.isDebugEnabled()) {
484             LOG.debug("resolve conflict: " + Arrays.asList(resolvedList));
485         }
486         if (RESOLVE_CONFLICTS_RECIPIENT.equalsIgnoreCase(this.getHandleResolveConflicts())) {
487             if (resolvedList != null) {
488                 for (int i = 0; i < resolvedList.length; i++) {
489                     this.addRecipient(resolvedList[i]);
490                 }
491             }
492         } else if (RESOLVE_CONFLICTS_ERROR.equalsIgnoreCase(this.getHandleResolveConflicts())) {
493             this.resolveConflictMessage = "resolveConflicts: " + Arrays.asList(resolvedList);
494         } else if (RESOLVE_CONFLICTS_WARN.equalsIgnoreCase(this.getHandleResolveConflicts())) {
495             LOG.warn("resolveConflicts: " + Arrays.asList(resolvedList));
496         }
497     }
498
499     private synchronized void addRecipient(STObject recipient) {
500         this.resolvedNameSet.add(recipient.getName().toLowerCase());
501         if (recipient instanceof STGroup) {
502             this.recipientGroupSet.add(recipient);
503         } else if (recipient instanceof STUser) {
504             this.recipientUserSet.add(recipient);
505         }
506     }
507
508     public synchronized void resolved(ResolveEvent resolveEvent) {
509         if (LOG.isDebugEnabled()) {
510             LOG.debug("resolved: " + resolveEvent.getResolved());
511         }
512         this.addRecipient(resolveEvent.getResolved());
513     }
514
515     public synchronized void resolveFailed(ResolveEvent resolveEvent) {
516         String JavaDoc[] failedNames = resolveEvent.getFailedNames();
517         if (LOG.isDebugEnabled()) {
518             LOG.debug("resolve failed: " + Arrays.asList(failedNames));
519         }
520         this.resolveFailMessage = "cannot resolve, reason "
521                          + resolveEvent.getReason() + ": " + Arrays.asList(failedNames);
522         if (RESOLVE_FAIL_WARN.equalsIgnoreCase(this.getHandleResolveFails())) {
523             LOG.warn(this.resolveFailMessage);
524         }
525     }
526
527     public synchronized void groupContentQueried(GroupContentEvent groupContentEvent) {
528         STObject[] eventGroupContent = groupContentEvent.getGroupContent();
529         if (eventGroupContent != null) {
530             for (int i = 0; i < eventGroupContent.length; i++) {
531                 // add directly to the user set as we are iterating over the goup set, so cannot modify it
532
// this means that we will not support groups within groups
533
if (eventGroupContent[i] instanceof STUser) {
534                     this.recipientUserSet.add(eventGroupContent[i]);
535                 } else {
536                     throw new UnsupportedOperationException JavaDoc("groups within groups not supported - found subgroup "
537                        + eventGroupContent[i].getName() + " while querying group "
538                        + groupContentEvent.getGroup().getName());
539                 }
540             }
541         }
542         this.groupContent = eventGroupContent;
543     }
544
545     public synchronized void queryGroupContentFailed(GroupContentEvent groupContentEvent) {
546         this.queryGroupContentFailMessage = "queryGroupContent failed for group "
547              + groupContentEvent.getGroup().getName()
548              + ", reason " + groupContentEvent.getReason();
549         if (QUERY_GROUP_CONTENT_FAIL_WARN.equalsIgnoreCase(this.getHandleQueryGroupContentFails())) {
550             LOG.warn(this.queryGroupContentFailMessage);
551         }
552     }
553
554 }
555
Popular Tags