Package net.sourceforge.cruisecontrol.publishers

Source Code of net.sourceforge.cruisecontrol.publishers.SametimeAnnouncementPublisher

/********************************************************************************
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 2003, ThoughtWorks, Inc.
* 651 W Washington Ave. Suite 600
* Chicago, IL 60661 USA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*     + Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*
*     + Redistributions in binary form must reproduce the above
*       copyright notice, this list of conditions and the following
*       disclaimer in the documentation and/or other materials provided
*       with the distribution.
*
*     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************/
package net.sourceforge.cruisecontrol.publishers;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

import net.sourceforge.cruisecontrol.CruiseControlException;
import net.sourceforge.cruisecontrol.util.XMLLogHelper;

import org.apache.log4j.Logger;

import com.lotus.sametime.announcement.AnnouncementService;
import com.lotus.sametime.community.CommunityService;
import com.lotus.sametime.community.Login;
import com.lotus.sametime.community.LoginEvent;
import com.lotus.sametime.community.LoginListener;
import com.lotus.sametime.core.comparch.DuplicateObjectException;
import com.lotus.sametime.core.comparch.STSession;
import com.lotus.sametime.core.types.STGroup;
import com.lotus.sametime.core.types.STObject;
import com.lotus.sametime.core.types.STUser;
import com.lotus.sametime.lookup.GroupContentEvent;
import com.lotus.sametime.lookup.GroupContentGetter;
import com.lotus.sametime.lookup.GroupContentListener;
import com.lotus.sametime.lookup.LookupService;
import com.lotus.sametime.lookup.ResolveEvent;
import com.lotus.sametime.lookup.ResolveListener;
import com.lotus.sametime.lookup.Resolver;

/**
* Publish (simple) build results by sending a Sametime announcement.
* <p>Requires Sametime 3.0 Java Toolkit.  See http://www-10.lotus.com/ldd/toolkits<br>
* In particular, requires STComm.jar
* @author Richard Lewis-Shell
*/
public class SametimeAnnouncementPublisher extends LinkEmailPublisher
             implements LoginListener, ResolveListener, GroupContentListener {
    private static final Logger LOG = Logger.getLogger(SametimeAnnouncementPublisher.class);

    public static final String RESOLVE_CONFLICTS_RECIPIENT = "recipient"; // default
    public static final String RESOLVE_CONFLICTS_IGNORE = "ignore";
    public static final String RESOLVE_CONFLICTS_WARN = "warn";
    public static final String RESOLVE_CONFLICTS_ERROR = "error";
   
    public static final String RESOLVE_FAIL_IGNORE = "ignore";
    public static final String RESOLVE_FAIL_WARN = "warn";
    public static final String RESOLVE_FAIL_ERROR = "error"; // default
   
    public static final String QUERY_GROUP_CONTENT_FAIL_IGNORE = "ignore";
    public static final String QUERY_GROUP_CONTENT_FAIL_WARN = "warn";
    public static final String QUERY_GROUP_CONTENT_FAIL_ERROR = "error"; // default
   
    // Configurable properties
   
    // Sametime community
    private String community;
    // whether to resolve addresses as users
    private boolean resolveUsers = true;
    // whether to resolve addresses as groups
    private boolean resolveGroups = true;
    // send to group contents, rather than the group itself
    private boolean useGroupContent = true;
    // valid values: recipient, ignore, fail   
    private String handleResolveConflicts = RESOLVE_CONFLICTS_RECIPIENT;
    // how to handle resolve failures
    private String handleResolveFails = RESOLVE_FAIL_ERROR;
    // how to handle query group content failures
    private String handleQueryGroupContentFails = QUERY_GROUP_CONTENT_FAIL_ERROR;
    // how long to wait (in seconds) before giving up on an interaction with the sametime server
    private int timeout = 10;
    // how long to sleep (in milliseconds) before looking for a response from the sametime server   
    private int sleepMillis = 5;
       
    // Internal state
   
    // Sametime components
    private STSession session;
    private CommunityService communityService;
    private AnnouncementService announcementService;
    private LookupService lookupService;

    // list of users/groups to resolve into STObjects
    private Set usernamesToResolveSet;
    // login, null if not logged in
    private Login login;
    // true if there is a login problem
    private boolean loginFailed;
    // list of resolved STUsers
    private Set recipientUserSet = null;
    // list of resolved STGroups
    private Set recipientGroupSet = null;
    // list of resovled user/group NAMES
    private Set resolvedNameSet = null;
    // used to temporarily hold group content (while getting)
    private STObject[] groupContent = null;
    // error messages constructed in a different thread, thrown as exceptions
    // in the main thread
    private String resolveFailMessage = null;
    private String resolveConflictMessage = null;
    private String queryGroupContentFailMessage = null;
   
    // rename the mailhost property
    public String getHost() {
        return this.getMailHost();
    }
   
    // rename the mailhost property
    public void setHost(String value) {
        this.setMailHost(value);
    }

    public String getCommunity() {
        return this.community;
    }

    public void setCommunity(String value) {
        this.community = value;
    }

    public boolean isResolveUsers() {
        return this.resolveUsers;
    }
   
    public void setResolveUsers(boolean value) {
        this.resolveUsers = value;
    }
   
    public boolean isResolveGroups() {
        return this.resolveGroups;
    }
   
    public void setResolveGroups(boolean value) {
        this.resolveGroups = value;
    }
   
    public boolean isUseGroupContent() {
        return this.useGroupContent;
    }
   
    public void setUseGroupContent(boolean value) {
        this.useGroupContent = value;
    }
   
    public String getHandleQueryGroupContentFails() {
        return this.handleQueryGroupContentFails;
    }

    public String getHandleResolveConflicts() {
        return this.handleResolveConflicts;
    }

    public String getHandleResolveFails() {
        return this.handleResolveFails;
    }

    public void setHandleQueryGroupContentFails(String string) {
        this.handleQueryGroupContentFails = string;
    }

    public void setHandleResolveConflicts(String value) {
        this.handleResolveConflicts = value;
    }

    public void setHandleResolveFails(String value) {
        this.handleResolveFails = value;
    }
   
    public int getTimeout() {
        return this.timeout;
    }

    private int getTimeoutMillis() {
        return this.getTimeout() * 1000;
    }

    public int getSleepMillis() {
        return sleepMillis;
    }

    public void setTimeout(int value) {
        timeout = value;
    }

    public void setSleepMillis(int value) {
        sleepMillis = value;
    }

    // override this so we can otherwise rely on EmailPublisher's validation
    // returnAddress makes no sense for Sametime
    public String getReturnAddress() {
        return "";
    }

    public void validate() throws CruiseControlException {
        if (this.getHost() == null) {
            throw new CruiseControlException("'host' not specified in configuration file.");
        }
        if (this.getUsername() == null) {
            throw new CruiseControlException("'username' not specified in configuration file.");
        }
       
        if (!this.getHandleResolveFails().equalsIgnoreCase(RESOLVE_FAIL_ERROR)
            && !this.getHandleResolveFails().equalsIgnoreCase(RESOLVE_FAIL_WARN)
            && !this.getHandleResolveFails().equalsIgnoreCase(RESOLVE_FAIL_IGNORE)) {
            throw new CruiseControlException("'handleResolveFails' attribute invalid."
                      + " - valid values are "
                      + RESOLVE_FAIL_ERROR + " | "
                      + RESOLVE_FAIL_WARN + " | "
                      + RESOLVE_FAIL_IGNORE);
        }
        if (!this.getHandleResolveConflicts().equalsIgnoreCase(RESOLVE_CONFLICTS_ERROR)
            && !this.getHandleResolveConflicts().equalsIgnoreCase(RESOLVE_CONFLICTS_WARN)
            && !this.getHandleResolveConflicts().equalsIgnoreCase(RESOLVE_CONFLICTS_IGNORE)
            && !this.getHandleResolveConflicts().equalsIgnoreCase(RESOLVE_CONFLICTS_RECIPIENT)) {
            throw new CruiseControlException("'handleResolveConflicts' attribute invalid"
                      + " - valid values are "
                      + RESOLVE_CONFLICTS_ERROR + " | "
                      + RESOLVE_CONFLICTS_WARN + " | "
                      + RESOLVE_CONFLICTS_IGNORE + " | "
                      + RESOLVE_CONFLICTS_RECIPIENT);
        }
        if (!this.getHandleQueryGroupContentFails().equalsIgnoreCase(QUERY_GROUP_CONTENT_FAIL_ERROR)
            && !this.getHandleQueryGroupContentFails().equalsIgnoreCase(QUERY_GROUP_CONTENT_FAIL_WARN)
            && !this.getHandleQueryGroupContentFails().equalsIgnoreCase(QUERY_GROUP_CONTENT_FAIL_IGNORE)) {
            throw new CruiseControlException("'handleQueryGroupContentFails' attribute invalid"
                      + " - valid values are "
                      + QUERY_GROUP_CONTENT_FAIL_ERROR + " | "
                      + QUERY_GROUP_CONTENT_FAIL_WARN + " | "
                      + QUERY_GROUP_CONTENT_FAIL_IGNORE);       
        }
    }

    // use the build results URL as the message content
    protected String createMessage(XMLLogHelper logHelper) {
        return this.getBuildResultsURL() == null ? null : super.createMessage(logHelper);
    }

    // not really sending mail, but LinkEmailPublisher has a lot of useful logic
    // to reuse - skipUsers, spamWhileBroken etc...
    protected void sendMail(String toList, String subject, String message, boolean important)
        throws CruiseControlException {
        LOG.info("Sending sametime notifications.");

        // turn the comma separated toList into a list of users/groups to be resolved
        this.usernamesToResolveSet = new HashSet();
        for (StringTokenizer strtok = new StringTokenizer(toList, ","); strtok.hasMoreTokens(); ) {
            String token = strtok.nextToken();
            this.usernamesToResolveSet.add(token.trim());
        }
   
        try {
            this.session = new STSession("CruiseControl build notification" + this);
        } catch (DuplicateObjectException ex) {
            throw new RuntimeException("cannot create sametime session" + ex);
        }
        this.session.loadSemanticComponents();
        this.session.start();

        this.communityService = (CommunityService) session.getCompApi(CommunityService.COMP_NAME);
        this.lookupService = (LookupService) session.getCompApi(LookupService.COMP_NAME);
        this.announcementService = (AnnouncementService) session.getCompApi(AnnouncementService.COMP_NAME);
        this.communityService.addLoginListener(this);
        if (this.getPassword() != null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("loginByPassword(" + this.getHost() + ", " + this.getUsername() + ", ****, "
                    + this.getCommunity() + ")");
            }
            this.communityService.loginByPassword(this.getHost(), this.getUsername(), this.getPassword(),
                                                  this.getCommunity());
        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("loginAsAnon(" + this.getHost() + ", " + this.getUsername()
                    + this.getCommunity() + ")");
            }
            this.communityService.loginAsAnon(this.getHost(), this.getUsername(), this.getCommunity()); // not tested
        }

        // if we lose the connection, just give up
        this.communityService.disableAutomaticReconnect();

        boolean bored = false;
        long waitStart = System.currentTimeMillis();
        while (!this.isLoggedIn() && !bored) {
            try {
                Thread.sleep(this.getSleepMillis());
            } catch (InterruptedException ex) {
                throw new RuntimeException("sleep interrupted: " + ex);
            }
            bored = System.currentTimeMillis() - waitStart > this.getTimeoutMillis();
        }
        if (bored && !this.isLoggedIn()) {
            throw new RuntimeException("bored waiting for login");
        }
   
        try {
            this.resolve();

            if (this.isUseGroupContent()) {
                this.getGroupContent();
            } else if (this.recipientGroupSet != null) {
                this.recipientUserSet.addAll(this.recipientGroupSet);
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("announce to: " + this.recipientUserSet);
            }
            // "\r\n" appears to be the end of line marker sametime uses
            String announcementMessage = message == null ? subject : subject + "\r\n" + message;
            this.announcementService.sendAnnouncement((STObject[]) this.recipientUserSet.toArray(
                                      new STObject[this.recipientUserSet.size()]), false, announcementMessage);
        } finally {
            try {
                this.communityService.logout();
            } finally {
                try {
                    this.session.stop();
                } finally {
                    this.session.unloadSession();
                }
            }
        }
    }

    private void resolve() throws CruiseControlException {
        Resolver resolver = lookupService.createResolver(false, false, this.isResolveUsers(), this.isResolveGroups());
        resolver.addResolveListener(this);
       
        try {
            this.recipientUserSet = new HashSet();
            this.recipientGroupSet = new HashSet();
            this.resolvedNameSet = new HashSet();
       
            if (LOG.isDebugEnabled()) {
                LOG.debug("resolving: " + this.usernamesToResolveSet);
            }
            resolver.resolve(
               (String[]) this.usernamesToResolveSet.toArray(new String[this.usernamesToResolveSet.size()]));
       
            // how long do we wait to resolve?
            boolean bored = false;
            long waitStart = System.currentTimeMillis();
            while (!this.isResolvedAllUsersAndGroups() && !bored && !this.isResolveError()) {
                try {
                    Thread.sleep(this.getSleepMillis());
                } catch (InterruptedException ex) {
                    throw new RuntimeException("sleep interrupted: " + ex);
                }
                bored = System.currentTimeMillis() - waitStart > this.getTimeoutMillis();
            }
            if (this.resolveFailMessage != null
                && RESOLVE_FAIL_ERROR.equalsIgnoreCase(this.getHandleResolveFails())) {
                throw new CruiseControlException(this.resolveFailMessage);
            }
            if (this.resolveConflictMessage != null
                && RESOLVE_CONFLICTS_ERROR.equalsIgnoreCase(this.getHandleResolveConflicts())) {
                throw new CruiseControlException(this.resolveConflictMessage);
            }
            if (bored && !this.isResolvedAllUsersAndGroups()) {
                throw new CruiseControlException("bored waiting for user/group resolving");
            }
        } finally {
            resolver.removeResolveListener(this);
        }
    }
   
    private boolean haveGroupContent() {
        return this.groupContent != null;
    }
   
    // convert the recipientGroupList into group content users
    private void getGroupContent() throws CruiseControlException {
        if (this.recipientGroupSet == null)  {
            return;
        }
        GroupContentGetter groupContentGetter = this.lookupService.createGroupContentGetter();
        groupContentGetter.addGroupContentListener(this);
        try {
            for (Iterator i = this.recipientGroupSet.iterator(); i.hasNext(); ) {
                this.groupContent = null;
                STGroup group = (STGroup) i.next();
                groupContentGetter.queryGroupContent(group);
                // how long do we wait to resolve?
                boolean bored = false;
                long waitStart = System.currentTimeMillis();
                while (!this.haveGroupContent() && !bored && !this.isQueryGroupContentError()) {
                    try {
                        Thread.sleep(this.getSleepMillis());
                    } catch (InterruptedException ex) {
                        throw new CruiseControlException("sleep interrupted: " + ex);
                    }
                    bored = System.currentTimeMillis() - waitStart > this.getTimeoutMillis();
                }
                if (this.isQueryGroupContentError()) {
                    throw new CruiseControlException(this.queryGroupContentFailMessage);
                }
                if (bored && !this.haveGroupContent()) {
                    throw new CruiseControlException("bored waiting to get group content for " + group);
                }
            }
        } finally {
            groupContentGetter.removeGroupContentListener(this);
        }
    }
   
    private synchronized boolean isQueryGroupContentError() {
        return this.queryGroupContentFailMessage != null
               && QUERY_GROUP_CONTENT_FAIL_ERROR.equalsIgnoreCase(this.getHandleQueryGroupContentFails());
    }
   
    private synchronized boolean isLoggedIn() {
        return this.communityService != null && this.communityService.isLoggedIn() && this.login != null;
    }
   
    // return true once all the users/groups to be resolved have been resolved
    private synchronized boolean isResolvedAllUsersAndGroups() {
        if (this.usernamesToResolveSet == null) {
            return true;
        }
        for (Iterator i = this.usernamesToResolveSet.iterator(); i.hasNext(); ) {
            String username = (String) i.next();
            if (!this.resolvedNameSet.contains(username.toLowerCase())) {
                return false;
            }
        }
        return true;
    }
   
    private synchronized boolean isResolveError() {
        return this.resolveFailMessage != null
               && RESOLVE_FAIL_ERROR.equalsIgnoreCase(this.getHandleResolveFails())
               || this.resolveConflictMessage != null
               && RESOLVE_CONFLICTS_ERROR.equalsIgnoreCase(this.getHandleResolveConflicts());
    }
       
    public synchronized void loggedIn(LoginEvent loginEvent) {
        this.login = loginEvent.getLogin();
    }

    public synchronized void loggedOut(LoginEvent arg0) {
        if (!this.isLoggedIn()) {
            this.loginFailed = true;
        }
        this.login = null;
    }

    public synchronized void resolveConflict(ResolveEvent resolveEvent) {
        STObject[] resolvedList = resolveEvent.getResolvedList();
        if (LOG.isDebugEnabled()) {
            LOG.debug("resolve conflict: " + Arrays.asList(resolvedList));
        }
        if (RESOLVE_CONFLICTS_RECIPIENT.equalsIgnoreCase(this.getHandleResolveConflicts())) {
            if (resolvedList != null) {
                for (int i = 0; i < resolvedList.length; i++) {
                    this.addRecipient(resolvedList[i]);
                }
            }
        } else if (RESOLVE_CONFLICTS_ERROR.equalsIgnoreCase(this.getHandleResolveConflicts())) {
            this.resolveConflictMessage = "resolveConflicts: " + Arrays.asList(resolvedList);
        } else if (RESOLVE_CONFLICTS_WARN.equalsIgnoreCase(this.getHandleResolveConflicts())) {
            LOG.warn("resolveConflicts: " + Arrays.asList(resolvedList));
        }
    }

    private synchronized void addRecipient(STObject recipient) {
        this.resolvedNameSet.add(recipient.getName().toLowerCase());
        if (recipient instanceof STGroup) {
            this.recipientGroupSet.add(recipient);
        } else if (recipient instanceof STUser) {
            this.recipientUserSet.add(recipient);
        }
    }

    public synchronized void resolved(ResolveEvent resolveEvent) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("resolved: " + resolveEvent.getResolved());
        }
        this.addRecipient(resolveEvent.getResolved());
    }

    public synchronized void resolveFailed(ResolveEvent resolveEvent) {
        String[] failedNames = resolveEvent.getFailedNames();
        if (LOG.isDebugEnabled()) {
            LOG.debug("resolve failed: " + Arrays.asList(failedNames));
        }
        this.resolveFailMessage = "cannot resolve, reason "
                         + resolveEvent.getReason() + ": " + Arrays.asList(failedNames);
        if (RESOLVE_FAIL_WARN.equalsIgnoreCase(this.getHandleResolveFails())) {
            LOG.warn(this.resolveFailMessage);
        }
    }

    public synchronized void groupContentQueried(GroupContentEvent groupContentEvent) {
        STObject[] eventGroupContent = groupContentEvent.getGroupContent();
        if (eventGroupContent != null) {
            for (int i = 0; i < eventGroupContent.length; i++) {
                // add directly to the user set as we are iterating over the goup set, so cannot modify it
                // this means that we will not support groups within groups
                if (eventGroupContent[i] instanceof STUser) {
                    this.recipientUserSet.add(eventGroupContent[i]);
                } else {
                    throw new UnsupportedOperationException("groups within groups not supported - found subgroup "
                       + eventGroupContent[i].getName() + " while querying group "
                       + groupContentEvent.getGroup().getName());
                }
            }
        }
        this.groupContent = eventGroupContent;
    }

    public synchronized void queryGroupContentFailed(GroupContentEvent groupContentEvent) {
        this.queryGroupContentFailMessage = "queryGroupContent failed for group "
             + groupContentEvent.getGroup().getName()
             + ", reason " + groupContentEvent.getReason();       
        if (QUERY_GROUP_CONTENT_FAIL_WARN.equalsIgnoreCase(this.getHandleQueryGroupContentFails())) {
            LOG.warn(this.queryGroupContentFailMessage);
        }
    }

}
TOP

Related Classes of net.sourceforge.cruisecontrol.publishers.SametimeAnnouncementPublisher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.