com.redhat.rhn.manager.channel.ChannelManager.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.rhn.manager.channel.ChannelManager.java

Source

/**
 * Copyright (c) 2009--2014 Red Hat, Inc.
 *
 * This software is licensed to you under the GNU General Public License,
 * version 2 (GPLv2). There is NO WARRANTY for this software, express or
 * implied, including the implied warranties of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
 * along with this software; if not, see
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
 *
 * Red Hat trademarks are not licensed under GPLv2. No permission is
 * granted to use or replicate Red Hat trademarks that are incorporated
 * in this software or its documentation.
 */
package com.redhat.rhn.manager.channel;

import com.redhat.rhn.common.conf.Config;
import com.redhat.rhn.common.conf.ConfigDefaults;
import com.redhat.rhn.common.db.datasource.CallableMode;
import com.redhat.rhn.common.db.datasource.DataList;
import com.redhat.rhn.common.db.datasource.DataResult;
import com.redhat.rhn.common.db.datasource.ModeFactory;
import com.redhat.rhn.common.db.datasource.SelectMode;
import com.redhat.rhn.common.db.datasource.WriteMode;
import com.redhat.rhn.common.hibernate.HibernateFactory;
import com.redhat.rhn.common.hibernate.LookupException;
import com.redhat.rhn.common.localization.LocalizationService;
import com.redhat.rhn.common.security.PermissionException;
import com.redhat.rhn.common.util.MethodUtil;
import com.redhat.rhn.common.validator.ValidatorException;
import com.redhat.rhn.domain.channel.Channel;
import com.redhat.rhn.domain.channel.ChannelArch;
import com.redhat.rhn.domain.channel.ChannelFactory;
import com.redhat.rhn.domain.channel.ChannelFamily;
import com.redhat.rhn.domain.channel.ChannelFamilyFactory;
import com.redhat.rhn.domain.channel.ChannelVersion;
import com.redhat.rhn.domain.channel.ClonedChannel;
import com.redhat.rhn.domain.channel.DistChannelMap;
import com.redhat.rhn.domain.channel.InvalidChannelRoleException;
import com.redhat.rhn.domain.channel.ProductName;
import com.redhat.rhn.domain.channel.ReleaseChannelMap;
import com.redhat.rhn.domain.common.CommonConstants;
import com.redhat.rhn.domain.common.VirtSubscriptionLevel;
import com.redhat.rhn.domain.errata.Errata;
import com.redhat.rhn.domain.kickstart.KickstartData;
import com.redhat.rhn.domain.org.Org;
import com.redhat.rhn.domain.org.OrgFactory;
import com.redhat.rhn.domain.rhnpackage.PackageEvr;
import com.redhat.rhn.domain.role.RoleFactory;
import com.redhat.rhn.domain.server.Server;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.frontend.action.channel.ssm.ChannelActionDAO;
import com.redhat.rhn.frontend.dto.ChannelOverview;
import com.redhat.rhn.frontend.dto.ChannelPerms;
import com.redhat.rhn.frontend.dto.ChannelTreeNode;
import com.redhat.rhn.frontend.dto.ChildChannelDto;
import com.redhat.rhn.frontend.dto.ErrataOverview;
import com.redhat.rhn.frontend.dto.EssentialChannelDto;
import com.redhat.rhn.frontend.dto.MultiOrgEntitlementsDto;
import com.redhat.rhn.frontend.dto.OrgChannelFamily;
import com.redhat.rhn.frontend.dto.OrgSoftwareEntitlementDto;
import com.redhat.rhn.frontend.dto.PackageDto;
import com.redhat.rhn.frontend.dto.PackageListItem;
import com.redhat.rhn.frontend.dto.PackageOverview;
import com.redhat.rhn.frontend.dto.SystemsPerChannelDto;
import com.redhat.rhn.frontend.listview.ListControl;
import com.redhat.rhn.frontend.listview.PageControl;
import com.redhat.rhn.frontend.xmlrpc.NoSuchChannelException;
import com.redhat.rhn.frontend.xmlrpc.ProxyChannelNotFoundException;
import com.redhat.rhn.manager.BaseManager;
import com.redhat.rhn.manager.entitlement.EntitlementManager;
import com.redhat.rhn.manager.errata.cache.ErrataCacheManager;
import com.redhat.rhn.manager.org.OrgManager;
import com.redhat.rhn.manager.rhnpackage.PackageManager;
import com.redhat.rhn.manager.rhnset.RhnSetDecl;
import com.redhat.rhn.manager.system.SystemManager;
import com.redhat.rhn.manager.user.UserManager;
import com.redhat.rhn.taskomatic.TaskomaticApi;
import com.redhat.rhn.taskomatic.task.TaskConstants;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.io.File;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * ChannelManager
 * @version $Rev$
 */
public class ChannelManager extends BaseManager {

    private static Logger log = Logger.getLogger(ChannelManager.class);

    public static final String QRY_ROLE_MANAGE = "manage";
    public static final String QRY_ROLE_SUBSCRIBE = "subscribe";

    // Valid RHEL 4 EUS Channel Versions (from rhnReleaseChannelMap):
    public static final Set<String> RHEL4_EUS_VERSIONS;
    static {
        RHEL4_EUS_VERSIONS = new HashSet<String>();
        RHEL4_EUS_VERSIONS.add("4AS");
        RHEL4_EUS_VERSIONS.add("4ES");
    }

    // Product name (also sometimes referred to as OS) is unfortunately very
    // convoluted. For RHEL, in rhnDistChannelMap is appears as "Red Hat Linux", in
    // rhnReleaseChannelMap it is "RHEL AS", and in rhnProductName it's label is "rhel".
    // If this starts to shift we may need to stop relying on constants and find some
    // way to look these up...
    public static final String RHEL_PRODUCT_NAME = "rhel";

    /**
     * Key used to identify the rhn-tools channel.  Used in searches to find the channel
     */
    public static final String TOOLS_CHANNEL_PACKAGE_NAME = Config.get().getString("tools_channel.package_name",
            "rhncfg");

    /**
     * Key used to identify the rhel-arch-server-vt-5 channel.
     * Used in searches to find the channel
     */
    public static final String VIRT_CHANNEL_PACKAGE_NAME = Config.get().getString("virt_channel.package_name",
            "libvirt");

    /**
     * Package name of rhn-virtualization-host
     */
    public static final String RHN_VIRT_HOST_PACKAGE_NAME = Config.get()
            .getString("tools_channel.virt_package_name", "rhn-virtualization-host");

    /**
     * OS name for the virt child channel.  rhnDistChannelMap.OS field.
     */
    public static final String VT_OS_PRODUCT = Config.get().getString("web.virt_product_os_name", "VT");

    private ChannelManager() {

    }

    /**
     * Refreshes the channel with the "newest" packages.  Newest isn't just
     * the latest versions, an errata could have obsoleted a package in which
     * case this would have removed said package from the channel.
     *
     * @param channel channel to be refreshed
     * @param label   the label
     */
    public static void refreshWithNewestPackages(Channel channel, String label) {
        refreshWithNewestPackages(channel.getId(), label);
    }

    /**
     * Refreshes the channel with the "newest" packages.  Newest isn't just
     * the latest versions, an errata could have obsoleted a package in which
     * case this would have removed said package from the channel.
     *
     * @param channelId identifies the channel to be refreshed
     * @param label     the label
     */
    public static void refreshWithNewestPackages(Long channelId, String label) {
        Channel chan = ChannelFactory.lookupById(channelId);
        ChannelFactory.refreshNewestPackageCache(channelId, label);
        if (chan != null) {
            ChannelManager.queueChannelChange(chan.getLabel(), label, label);
        }
    }

    /**
     * Clones the "newest" packages to the clone channel.
     * The reason is to speed up the process, becasue calling
     * rhn_channel.refresh_newest_package
     * takes two minutes for channel with 11000 packages
     *
     * @param fromChannelId original channel id
     * @param toChannel cloned channel
     * @param label label for taskomatic repo_regen request
     */
    public static void cloneNewestPackages(Long fromChannelId, Channel toChannel, String label) {
        ChannelFactory.cloneNewestPackageCache(fromChannelId, toChannel.getId());
        ChannelManager.queueChannelChange(toChannel.getLabel(), label, "clone channel");
    }

    /**
     * Returns a list channel entitlements
     * @param orgId The users org ID
     * @param pc The PageControl
     * @return channel entitlements
     */
    public static DataResult<ChannelOverview> entitlements(Long orgId, PageControl pc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channel_entitlements");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", orgId);
        return makeDataResult(params, params, pc, m, ChannelOverview.class);
    }

    /**
     * Given an org it returns all the channel family subscription information
     * pertaining to that org.
     * @param org the org whose subscriptsion you are interested
     * @return channel family subscriptions/entitlement information.
     */
    public static List<OrgChannelFamily> listChannelFamilySubscriptionsFor(Org org) {
        List<OrgChannelFamily> ret = new LinkedList<OrgChannelFamily>();
        List<ChannelOverview> orgEntitlements = ChannelManager.entitlements(org.getId(), null);

        List<ChannelOverview> satEntitlements = ChannelManager.entitlements(OrgFactory.getSatelliteOrg().getId(),
                null);

        // Reformat it into a map for easy lookup
        Map<Long, ChannelOverview> orgMap = new HashMap<Long, ChannelOverview>();
        for (ChannelOverview orgEnt : orgEntitlements) {
            orgMap.put(orgEnt.getId(), orgEnt);
        }

        for (ChannelOverview sato : satEntitlements) {
            OrgChannelFamily ocf = new OrgChannelFamily();

            ocf.setSatelliteCurrentMembers(sato.getCurrentMembers());
            ocf.setSatelliteMaxMembers(sato.getMaxMembers());
            ocf.setSatelliteCurrentFlex(sato.getCurrentFlex());
            ocf.setSatelliteMaxFlex(sato.getMaxFlex());
            ocf.setId(sato.getId());
            ocf.setName(sato.getName());
            ocf.setLabel(sato.getLabel());

            ChannelOverview orgo = orgMap.get(sato.getId());
            if (orgo == null) {
                ocf.setCurrentMembers(new Long(0));
                ocf.setMaxMembers(new Long(0));
                ocf.setMaxFlex(0L);
                ocf.setCurrentFlex(0L);
            } else {
                ocf.setCurrentMembers(orgo.getCurrentMembers());
                ocf.setMaxMembers(orgo.getMaxMembers());
                ocf.setMaxFlex(orgo.getMaxFlex());
                ocf.setCurrentFlex(orgo.getCurrentFlex());
            }
            if (ocf.getSatelliteMaxMembers() != null) {
                ret.add(ocf);
            }
        }
        return ret;
    }

    /**
     * Returns a list channel entitlements for all orgs.
     * @return channel entitlements for multiorgs
     */
    public static DataList<MultiOrgEntitlementsDto> entitlementsForAllMOrgs() {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channel_entitlements_for_all_m_orgs");
        return DataList.getDataList(m, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
    }

    /**
     * Return a ChannelOverview for all orgs using the given channel family.
     * @param entitlementId Channel family ID.
     * @return List of ChannelOverview objects.
     */
    public static List<ChannelOverview> getEntitlementForAllOrgs(Long entitlementId) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channel_entitlement_for_all_orgs");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("entitlement_id", entitlementId);
        return makeDataResult(params, params, null, m, ChannelOverview.class);
    }

    /**
     * Given a channel family, this method returns entitlement information on a per org
     * basis. If a particular org does not have any entitlements in the family, it
     * will <strong>not</strong> be listed.
     *
     * @param cf   the channel family
     * @param user the user needed for access privilege
     * @return the lists the entitlement information for the given channel family
     *         for all orgs that have <strong>at least one entitlement on the
     *         family.</strong>
     */
    public static List<OrgSoftwareEntitlementDto> listEntitlementsForAllOrgs(ChannelFamily cf, User user) {
        List<OrgSoftwareEntitlementDto> ret = new LinkedList<OrgSoftwareEntitlementDto>();

        List<ChannelOverview> entitlementUsage = ChannelManager.getEntitlementForAllOrgs(cf.getId());

        // Create a mapping of org ID's to the channel overview returned, we'll need this
        // when iterating the list of all orgs shortly:
        Map<Long, ChannelOverview> orgEntitlementUsage = new HashMap<Long, ChannelOverview>();
        for (ChannelOverview o : entitlementUsage) {
            orgEntitlementUsage.put(o.getOrgId(), o);
        }
        Org satelliteOrg = OrgFactory.getSatelliteOrg();
        ChannelOverview satelliteOrgOverview = ChannelManager.getEntitlement(satelliteOrg.getId(), cf.getId());
        if (satelliteOrgOverview == null) {
            throw new RuntimeException(
                    "Satellite org does not" + "appear to have been allocated entitlement:" + cf.getId());
        }

        List<Org> allOrgs = OrgManager.allOrgs(user);
        for (Org org : allOrgs) {
            if (orgEntitlementUsage.containsKey(org.getId())) {
                ChannelOverview co = orgEntitlementUsage.get(org.getId());
                if (co.getMaxMembers() == 0 && co.getMaxFlex() == 0) {
                    continue;
                }
                ret.add(makeOrgSoftwareEntitlement(co, org, satelliteOrgOverview));
            }
        }

        return ret;
    }

    private static OrgSoftwareEntitlementDto makeOrgSoftwareEntitlement(ChannelOverview co, Org org,
            ChannelOverview satelliteOrgOverview) {
        OrgSoftwareEntitlementDto seDto = new OrgSoftwareEntitlementDto();
        seDto.setOrg(org);
        seDto.setCurrentMembers(co.getCurrentMembers());
        seDto.setMaxMembers(co.getMaxMembers());
        if (co.getMaxMembers() != null && satelliteOrgOverview.getFreeMembers() != null) {
            seDto.setMaxPossibleAllocation(co.getMaxMembers() + satelliteOrgOverview.getFreeMembers());
        }

        seDto.setCurrentFlex(co.getCurrentFlex());
        seDto.setMaxFlex(co.getMaxFlex());

        if (co.getMaxFlex() != null && satelliteOrgOverview.getFreeFlex() != null) {
            seDto.setMaxPossibleFlexAllocation(co.getMaxFlex() + satelliteOrgOverview.getFreeFlex());
        }
        return seDto;
    }

    /**
     * Given a channel family, this method returns entitlement information on a per org
     * basis. This call will return all organizations, even if it does not have any
     * entitlements on the family.
     *
     * @param cf   the channel family
     * @param user the user needed for access privilege
     * @return lists the entitlement information for the given channel family for
     *         all orgs.
     */
    public static List<OrgSoftwareEntitlementDto> listEntitlementsForAllOrgsWithEmptyOrgs(ChannelFamily cf,
            User user) {
        List<OrgSoftwareEntitlementDto> ret = new LinkedList<OrgSoftwareEntitlementDto>();

        List<ChannelOverview> entitlementUsage = ChannelManager.getEntitlementForAllOrgs(cf.getId());

        // Create a mapping of org ID's to the channel overview returned, we'll need this
        // when iterating the list of all orgs shortly:
        Map<Long, ChannelOverview> orgEntitlementUsage = new HashMap<Long, ChannelOverview>();
        for (ChannelOverview o : entitlementUsage) {
            orgEntitlementUsage.put(o.getOrgId(), o);
        }
        Org satelliteOrg = OrgFactory.getSatelliteOrg();
        ChannelOverview satelliteOrgOverview = ChannelManager.getEntitlement(satelliteOrg.getId(), cf.getId());
        if (satelliteOrgOverview == null) {
            throw new RuntimeException(
                    "Satellite org does not" + "appear to have been allocated entitlement:" + cf.getId());
        }

        List<Org> allOrgs = OrgManager.allOrgs(user);
        for (Org org : allOrgs) {
            if (orgEntitlementUsage.containsKey(org.getId())) {
                ChannelOverview co = orgEntitlementUsage.get(org.getId());
                ret.add(makeOrgSoftwareEntitlement(co, org, satelliteOrgOverview));
            } else {
                OrgSoftwareEntitlementDto seDto = new OrgSoftwareEntitlementDto();
                seDto.setOrg(org);
                seDto.setCurrentMembers(0L);
                seDto.setMaxMembers(0L);
                seDto.setMaxPossibleAllocation(satelliteOrgOverview.getFreeMembers());
                seDto.setCurrentFlex(0L);
                seDto.setMaxFlex(0L);
                seDto.setMaxPossibleFlexAllocation(satelliteOrgOverview.getFreeFlex());
                ret.add(seDto);
            }

        }
        return ret;
    }

    /**
     * Returns a specifically requested entitlement
     * @param orgId The user's org ID
     * @param entitlementId the id of the entitlement
     * @return the Channel Entitlement
     */
    public static ChannelOverview getEntitlement(Long orgId, Long entitlementId) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channel_entitlement");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", orgId);
        params.put("entitlement_id", entitlementId);
        DataResult<ChannelOverview> dr = m.execute(params);

        if (dr != null && !dr.isEmpty()) {
            return dr.get(0);
        }
        return null;
    }

    /**
     * Returns a list of ChannelTreeNodes that have orgId null
     *      or has a parent with org_id null
     * @param user who we are requesting channels for
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's
     */
    public static DataResult<ChannelTreeNode> vendorChannelTree(User user, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "vendor_channel_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", user.getOrg().getId());
        params.put("user_id", user.getId());

        DataResult<ChannelTreeNode> dr = makeDataResult(params, params, lc, m, ChannelTreeNode.class);
        Collections.sort(dr);
        return dr;
    }

    /**
     * Returns a list of ChannelTreeNodes that have orgId null
     *      or has a prarent with org_id null
     * @param user who we are requesting Red Hat channels for
     * @param serverCount the number of systems registered to that channel for it to
     *      be popular
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's
     */
    public static DataResult<ChannelTreeNode> popularChannelTree(User user, Long serverCount, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "popular_channel_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", user.getOrg().getId());
        params.put("user_id", user.getId());
        params.put("server_count", serverCount);

        DataResult<ChannelTreeNode> dr = makeDataResult(params, params, lc, m, ChannelTreeNode.class);
        Collections.sort(dr);
        return dr;
    }

    /**
     * Returns a list of ChannelTreeNodes that have orgId null
     *      or has a parent with org_id null
     * @param user who we are requesting Red Hat channels for
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's
     */
    public static DataResult<ChannelTreeNode> myChannelTree(User user, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "my_channel_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", user.getOrg().getId());
        params.put("user_id", user.getId());

        return makeDataResult(params, params, lc, m, ChannelTreeNode.class);
    }

    /**
     * Returns a list of ChannelTreeNodes containing all channels
     * the trusted org is consuming from a specific org
     * @param org Org that is sharing the channels
     * @param trustOrg org that is consuming the shared channels
     * @param user User of the sharing Org
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's
     */
    public static DataResult<ChannelTreeNode> trustChannelConsume(Org org, Org trustOrg, User user,
            ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "trust_channel_consume");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", trustOrg.getId());
        params.put("user_id", user.getId());
        params.put("org_id2", org.getId());
        return makeDataResult(params, params, lc, m, ChannelTreeNode.class);
    }

    /**
     * Returns a list of ChannelTreeNodes containing all channels
     * the user can see
     * @param user who we are requesting channels for
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's
     */
    public static DataResult<ChannelTreeNode> allChannelTree(User user, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "all_channel_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", user.getOrg().getId());
        params.put("user_id", user.getId());

        return makeDataResult(params, params, lc, m, ChannelTreeNode.class);
    }

    /**
     * Returns a list of channels owned by the user.
     *
     * @param user cannot be <code>null</code>
     * @return list of maps containing the channel data
     */
    public static DataResult<ChannelTreeNode> ownedChannelsTree(User user) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "owned_channels_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("user_id", user.getId());

        return makeDataResult(params, params, null, m, ChannelTreeNode.class);
    }

    /**
     * Returns a list of ChannelTreeNodes containing shared channels
     * the user can see
     * @param user who we are requesting channels for
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's
     */
    public static DataResult<ChannelTreeNode> sharedChannelTree(User user, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "shared_channel_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", user.getOrg().getId());
        params.put("user_id", user.getId());

        return makeDataResult(params, params, lc, m, ChannelTreeNode.class);
    }

    /**
     * Returns a list of ChannelTreeNodes containing end-of-life
     * retired channels the user can see
     * @param user who we are requesting channels for
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's
     */
    public static DataResult<ChannelTreeNode> retiredChannelTree(User user, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "retired_channel_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", user.getOrg().getId());
        params.put("user_id", user.getId());

        return makeDataResult(params, params, lc, m, ChannelTreeNode.class);
    }

    /**
     * Returns a list of channels and their parents who are in a particular
     * channel family/entitlement
     * @param user who we are requesting channels for
     * @param familyId Id of the family we want a tree for
     * @param lc ListControl to use
     * @return list of ChannelTreeNode's representing the channel family
     */
    public static DataResult<ChannelTreeNode> channelFamilyTree(User user, Long familyId, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channel_family_tree");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("user_id", user.getId());
        params.put("family_id", familyId);
        params.put("org_id", user.getOrg().getId());

        return makeDataResult(params, params, lc, m, ChannelTreeNode.class);
    }

    /**
     * Returns a dataresult containing the channels in an org.
     * @param orgId The org in question
     * @param pc page control for the user
     * @return Returns a data result containing ChannelOverview dtos
     */
    public static DataResult<ChannelOverview> channelsOwnedByOrg(Long orgId, PageControl pc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channels_owned_by_org");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", orgId);
        return makeDataResult(params, null, pc, m, ChannelOverview.class);
    }

    /**
     * Returns the package ids for packages relevant to a channel for a published errata
     * @param channelId The id for the channel in question
     * @param e the errata in question
     * @return Returns the ids for relevant packages
     */
    public static DataResult<Long> relevantPackages(Long channelId, Errata e) {
        SelectMode m;

        if (e.isPublished()) {
            m = ModeFactory.getMode("Channel_queries", "relevant_packages_for_channel_published");
        } else {
            m = ModeFactory.getMode("Channel_queries", "relevant_packages_for_channel_unpublished");
        }

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", channelId);
        params.put("eid", e.getId());
        return makeDataResult(params, null, null, m, Long.class);
    }

    /**
     * Returns a Channel object with the given id and user
     * @param cid The id for the channel you want
     * @param userIn The User looking for the channel
     * @return Returns the channel with the given id
     */
    public static Channel lookupByIdAndUser(Long cid, User userIn) {
        Channel channel = ChannelFactory.lookupByIdAndUser(cid, userIn);
        if (channel == null) {
            LocalizationService ls = LocalizationService.getInstance();
            LookupException e = new LookupException("User " + userIn.getId() + " does not have access to channel "
                    + cid + " or the channel does not exist");
            e.setLocalizedTitle(ls.getMessage("lookup.jsp.title.channel"));
            e.setLocalizedReason1(ls.getMessage("lookup.jsp.reason1.channel"));
            e.setLocalizedReason2(ls.getMessage("lookup.jsp.reason2.channel"));
            throw e;
        }
        return channel;
    }

    /**
     * Returns a Channel object with the given label and user
     * @param label The label for the channel you want
     * @param userIn The User looking for the channel
     * @return Returns the channel with the given id
     */
    public static Channel lookupByLabelAndUser(String label, User userIn) {
        Channel channel = ChannelFactory.lookupByLabelAndUser(label, userIn);
        if (channel == null) {
            LocalizationService ls = LocalizationService.getInstance();
            LookupException e = new LookupException("User " + userIn.getId() + " does not have access to channel "
                    + label + " or the channel does not exist");
            e.setLocalizedTitle(ls.getMessage("lookup.jsp.title.channel"));
            e.setLocalizedReason1(ls.getMessage("lookup.jsp.reason1.channel"));
            e.setLocalizedReason2(ls.getMessage("lookup.jsp.reason2.channel"));
            throw e;
        }
        return channel;
    }

    /**
     * list of base channels visible to the org
     * @param org Org to check
     * @return List of Base ChannelOverviews visible to the org
     */
    public static List<ChannelOverview> listBaseChannelsForOrg(Org org) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "list_base_channels_for_org");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", org.getId());
        return m.execute(params);
    }

    /**
     * list of child channels visible to the org for a given base channel
     * @param org Org to check
     * @param baseId Cid of the base channel
     * @return List of Child ChannelOverviews visible to the org
     */
    public static List<ChannelOverview> listChildChannelsForOrgAndBase(Org org, Long baseId) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "list_child_channels_for_org_and_base");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", org.getId());
        params.put("base_id", baseId);
        return m.execute(params);
    }

    /**
     * returns a list of channel ids that the user can subscribe to
     * @param user User to check
     * @return List of all channel ids that are subscribable for this user
     */
    public static Set<Long> subscribableChannelIdsForUser(User user) {
        Set<Long> ret = new HashSet<Long>();

        //Setup items for the query
        SelectMode m = ModeFactory.getMode("Channel_queries", "user_subscribe_perms");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("user_id", user.getId());
        params.put("org_id", user.getOrg().getId());

        //Execute the query
        DataResult<ChannelPerms> subscribable = m.execute(params);

        for (ChannelPerms perm : subscribable) {
            if (perm.isHasPerm()) {
                ret.add(perm.getId());
            }
        }

        return ret;
    }

    /**
     * channelsForUser returns a list containing the names of the channels
     * that this user has permissions to. If the user doesn't have permissions
     * to any channels, this method returns an empty list.
     * @param user The user in question
     * @return Returns the list of names of channels this user has permission to,
     * an empty list otherwise.
     */
    public static List<String> channelsForUser(User user) {
        //subscribableChannels is the list we'll be returning
        List<String> subscribableChannels = new ArrayList<String>();

        //Setup items for the query
        SelectMode m = ModeFactory.getMode("Channel_queries", "user_subscribe_perms");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("user_id", user.getId());
        params.put("org_id", user.getOrg().getId());

        //Execute the query
        DataResult<ChannelPerms> subscribable = m.execute(params);

        /*
         * We now need to go through the subscribable DataResult and
         * add the names of the channels this user has permissions to
         * to the subscribableChannels list.
         */
        Iterator<ChannelPerms> i = subscribable.iterator();
        while (i.hasNext()) {
            ChannelPerms perms = i.next();
            //if the user has permissions for this channel
            if (perms.isHasPerm()) {
                //add the name to the list
                subscribableChannels.add(perms.getName());
            }
        }

        return subscribableChannels;
    }

    /**
     * Returns the list of Channel ids which the given orgid has access to.
     * @param orgid Org id
     * @param cid Base Channel id.
     * @return the list of Channel ids which the given orgid has access to.
     */
    public static List<Channel> userAccessibleChildChannels(Long orgid, Long cid) {
        return ChannelFactory.getUserAcessibleChannels(orgid, cid);
    }

    /**
     * Get the list of Channels with clonable Errata
     * @param org org we want to search against
     * @return List of com.redhat.rhn.domain.Channel objects
     */
    public static List<ClonedChannel> getChannelsWithClonableErrata(Org org) {
        return ChannelFactory.getChannelsWithClonableErrata(org);
    }

    /**
     * Get the list of Channels accessible by an org
     * @param orgid The id of the org
     * @return List of accessible channels
     */
    public static List<Channel> getChannelsAccessibleByOrg(Long orgid) {
        return ChannelFactory.getAccessibleChannelsByOrg(orgid);
    }

    /**
     * Returns list of proxy channel names for a given version
     * @param version proxy version
     * @param server Server
     * @return list of proxy channel names for a given version
     */
    public static Channel getProxyChannelByVersion(String version, Server server) {

        ChannelFamily proxyFamily = ChannelFamilyFactory
                .lookupByLabel(ChannelFamilyFactory.PROXY_CHANNEL_FAMILY_LABEL, null);

        if (proxyFamily == null || proxyFamily.getChannels() == null || proxyFamily.getChannels().isEmpty()) {
            if (!ConfigDefaults.get().isSpacewalk()) {
                throw new ProxyChannelNotFoundException();
            }
            return null;
        }

        /* We search for a proxy channel whose version equals the version of
         * proxy trying to activate and whose parent channel is our server's basechannel.
         * This will be the channel we attempt to subscribe the server to.
         */
        for (Channel proxyChan : proxyFamily.getChannels()) {
            if (proxyChan.getProduct() != null && proxyChan.getProduct().getVersion().equals(version)
                    && proxyChan.getParentChannel().equals(server.getBaseChannel())) {
                return proxyChan;
            }
        }
        if (!ConfigDefaults.get().isSpacewalk()) {
            throw new ProxyChannelNotFoundException();
        }

        return null;
    }

    /**
     * Returns the list of all channels the user can see.
     * @param user User whose channels are sought.
     * @return the list of all channels the user can see as a DataResult
     *
     */
    public static List<Map<String, Object>> allChannelsTree(User user) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "all_channels_tree");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("user_id", user.getId());

        return m.execute(params);
    }

    /**
     * Returns list of ChannelArches
     * @return list of ChannelArches
     * @see com.redhat.rhn.domain.channel.ChannelArch
     */
    public static List<ChannelArch> getChannelArchitectures() {
        return ChannelFactory.getChannelArchitectures();
    }

    /**
     * Deletes a software channel
     * @param user User with access to delete the channel.
     * @param label Channel label
     * @throws InvalidChannelRoleException thrown if User does not have access
     * to delete channel.
     * @throws NoSuchChannelException thrown if no channel exists with the
     * given label
     */
    public static void deleteChannel(User user, String label)
            throws InvalidChannelRoleException, NoSuchChannelException {

        Channel toRemove = ChannelFactory.lookupByLabel(user.getOrg(), label);

        if (toRemove == null) {
            throw new NoSuchChannelException();
        }
        if (toRemove.getOrg() == null) {
            throw new PermissionException(
                    LocalizationService.getInstance().getMessage("api.channel.delete.redhat"));
        }
        if (verifyChannelAdmin(user, toRemove.getId())) {
            if (!ChannelFactory.listAllChildrenForChannel(toRemove).isEmpty()) {
                throw new PermissionException(
                        LocalizationService.getInstance().getMessage("api.channel.delete.haschild"));
            }
            if (toRemove.containsDistributions()) {
                ValidatorException.raiseException("message.channel.cannot-be-deleted.has-distros");

            }
            ChannelManager.unscheduleEventualRepoSync(toRemove, user);
            ChannelManager.queueChannelChange(label, user.getLogin(), "java::deleteChannel");
            ChannelFactory.remove(toRemove);
        }
    }

    /**
     * Unschedule eventual repo sync schedule
     * @param channel relevant channel
     * @param user executive
     */
    public static void unscheduleEventualRepoSync(Channel channel, User user) {
        TaskomaticApi tapi = new TaskomaticApi();
        try {
            String cronExpr = tapi.getRepoSyncSchedule(channel, user);
            if (!StringUtils.isEmpty(cronExpr)) {
                log.info("Unscheduling repo sync schedule with " + cronExpr + " for channel " + channel.getLabel());
                tapi.unscheduleRepoSync(channel, user);
            }
        } catch (Exception e) {
            log.warn("Failed to unschedule repo sync for channel " + channel.getLabel());
        }
    }

    /**
     * Verify that a user is a channel admin
     * @param user the user to verify
     * @param cid the channel id to verify
     * @return true if the user is an admin of this channel
     * @throws InvalidChannelRoleException if the user does not have perms
     */
    public static boolean verifyChannelAdmin(User user, Long cid) throws InvalidChannelRoleException {

        if (user.hasRole(RoleFactory.CHANNEL_ADMIN)) {
            return true;
        }

        return verifyChannelRole(user, cid, QRY_ROLE_MANAGE);
    }

    /**
     * Adds the subscribe role for the passed in user for the passed in channel.
     * @param user The user in question.
     * @param channel The channel in question.
     */
    public static void addSubscribeRole(User user, Channel channel) {
        if (verifyChannelSubscribe(user, channel.getId())) {
            return; //user already has subscribe perms to this channel
        }
        //Insert row into rhnChannelPermission
        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "grant_channel_permission");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("user_id", user.getId());
        params.put("cid", channel.getId());
        params.put("role_label", QRY_ROLE_SUBSCRIBE);
        m.executeUpdate(params);
    }

    /**
     * Removes the subscribe role from the passed in user for the passed in channel.
     * @param user The user in question.
     * @param channel The channel in question.
     */
    public static void removeSubscribeRole(User user, Channel channel) {
        if (!verifyChannelSubscribe(user, channel.getId())) {
            return; //user doesn't have subscribe perms to begin with
        }
        //Delete row from rhnChannelPermission
        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "revoke_channel_permission");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("user_id", user.getId());
        params.put("cid", channel.getId());
        params.put("role_label", QRY_ROLE_SUBSCRIBE);
        m.executeUpdate(params);
    }

    /**
     * Adds the mange role for the passed in user for the passed in channel.
     * @param user The user in question.
     * @param channel The channel in question.
     */
    public static void addManageRole(User user, Channel channel) {
        if (verifyChannelManage(user, channel.getId())) {
            return; //user already has subscribe perms to this channel
        }
        //Insert row into rhnChannelPermission
        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "grant_channel_permission");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("user_id", user.getId());
        params.put("cid", channel.getId());
        params.put("role_label", QRY_ROLE_MANAGE);
        m.executeUpdate(params);
    }

    /**
     * Removes the manage role from the passed in user for the passed in channel.
     * @param user The user in question.
     * @param channel The channel in question.
     */
    public static void removeManageRole(User user, Channel channel) {
        if (!verifyChannelManage(user, channel.getId())) {
            return; //user doesn't have subscribe perms to begin with
        }
        //Delete row from rhnChannelPermission
        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "revoke_channel_permission");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("user_id", user.getId());
        params.put("cid", channel.getId());
        params.put("role_label", QRY_ROLE_MANAGE);
        m.executeUpdate(params);
    }

    /**
     * Makes sure the passed in user has subscribe permissions to the channel with the
     * given id.
     * @param user The user in question
     * @param cid The id for the channel in question
     * @return Returns true if the user has permission, false otherwise
     */
    public static boolean verifyChannelSubscribe(User user, Long cid) {

        try {
            return verifyChannelRole(user, cid, QRY_ROLE_SUBSCRIBE);
        } catch (InvalidChannelRoleException e) {
            /*
             * We don't really care what the reason is for why this user doesn't have
             * access to this channel, so catch the exception, log it, and simply
             * return false.
             */
            StringBuilder msg = new StringBuilder("User: ");
            msg.append(user.getLogin());
            msg.append(" either does not have subscribe privileges to Channel: ");
            msg.append(cid);
            msg.append(" or ChannelManager.QRY_ROLE_SUBSCRIBE is defined wrong.");
            log.debug(msg.toString(), e);
            return false;
        }
    }

    /**
     * Makes sure the passed in user has manage permissions to the channel with the
     * given id.
     * @param user The user in question
     * @param cid The id for the channel in question
     * @return Returns true if the user has permission, false otherwise
     */
    public static boolean verifyChannelManage(User user, Long cid) {

        try {
            return verifyChannelRole(user, cid, QRY_ROLE_MANAGE);
        } catch (InvalidChannelRoleException e) {
            /*
             * We don't really care what the reason is for why this user doesn't have
             * access to this channel, so catch the exception, log it, and simply
             * return false.
             */
            StringBuilder msg = new StringBuilder("User: ");
            msg.append(user.getLogin());
            msg.append(" either does not have subscribe privileges to Channel: ");
            msg.append(cid);
            msg.append(" or ChannelManager.QRY_ROLE_MANAGE is defined wrong.");
            log.debug(msg.toString(), e);
            return false;
        }
    }

    /**
     * Check to see if the channel passed in is subscribable by the Server passed in for
     * *free* without costing an entitlement.  Criteria:
     *
     *  1) if server is a virtual guest
     *  2) the host of the guest has the Virtualization or VirtualizationPlatform
     *     entitlement
     *  3) the host's system entitlement has to match the type of the Channel's
     *     ChannelFamily's VirtSubLevel type.
     *
     * @param serverIdIn to check against the channelIn
     * @param channelIn channelIn to check against the server
     * @return boolean if its free or not
     */
    public static boolean isChannelFreeForSubscription(Long serverIdIn, Channel channelIn) {

        if (log.isDebugEnabled()) {
            log.debug("isChannelFreeForSubscription.start: " + channelIn.getLabel());
        }

        // this mode query takes care of conditions 1 and 2.
        // if the system is not a guest or the host is not known or
        // the host is not entitled with either Virtualization or
        // VirtualizationPlatform then it'll return null and we'll exit
        SelectMode m = ModeFactory.getMode("System_queries", "host_virtual_entitlement_for_guest");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("sid", serverIdIn);
        List<Map<String, String>> results = m.execute(params);

        if (results == null || results.size() == 0) {
            log.debug("server is not a guest or host is not virtually entitled, " + "returning false");
            return false;
        }
        String hostVirtEntitlement = results.get(0).get("label");

        Set<VirtSubscriptionLevel> levels = channelIn.getChannelFamily().getVirtSubscriptionLevels();
        if (levels == null || levels.size() == 0) {
            log.debug("Channel has no virtsublevel. returning false");
            return false;
        }
        if (log.isDebugEnabled()) {
            log.debug("levels   : " + levels);
            log.debug("host.ent: " + hostVirtEntitlement);
        }
        if (hostVirtEntitlement.equals(EntitlementManager.VIRTUALIZATION_ENTITLED)) {
            log.debug("host has virt");
            Iterator<VirtSubscriptionLevel> i = levels.iterator();
            while (i.hasNext()) {
                VirtSubscriptionLevel level = i.next();
                if (level.equals(CommonConstants.getVirtSubscriptionLevelFree())) {
                    log.debug("Channel has virt and host has virt, returning true");
                    return true;
                }
            }
        }
        if (hostVirtEntitlement.equals(EntitlementManager.VIRTUALIZATION_PLATFORM_ENTITLED)) {
            log.debug("host has virt-plat");
            Iterator<VirtSubscriptionLevel> i = levels.iterator();
            while (i.hasNext()) {
                VirtSubscriptionLevel level = i.next();
                if (level.equals(CommonConstants.getVirtSubscriptionLevelPlatformFree())) {
                    log.debug("Channel has virt-plat and host virt-plat, returning true");
                    return true;
                }
            }
        }

        log.debug("No criteria match.  returning false");
        return false;
    }

    private static boolean verifyChannelRole(User user, Long cid, String role) throws InvalidChannelRoleException {

        CallableMode m = ModeFactory.getCallableMode("Channel_queries", "verify_channel_role");

        Map<String, Object> inParams = new HashMap<String, Object>();
        inParams.put("cid", cid);
        inParams.put("user_id", user.getId());
        inParams.put("role", role);

        Map<String, Integer> outParams = new HashMap<String, Integer>();
        outParams.put("result", new Integer(Types.VARCHAR));
        Map<String, Object> result = m.execute(inParams, outParams);

        String reason = (String) result.get("result");
        if (reason != null) {
            throw new InvalidChannelRoleException(reason);
        }
        return true;
    }

    /**
     * Returns true if the given channel is globally subscribable for the
     * given org.
     * @param user User
     * @param chanLabel label of Channel to validate
     * @return true if the given channel is globally subscribable for the
     */
    public static boolean isGloballySubscribable(User user, String chanLabel) {
        Channel c = lookupByLabelAndUser(chanLabel, user);
        return ChannelFactory.isGloballySubscribable(user.getOrg(), c);
    }

    /**
     * Returns the Channel whose label matches the given label.
     * @param org The org of the user looking up the channel
     * @param label Channel label sought.
     * @return the Channel whose label matches the given label.
     */
    public static Channel lookupByLabel(Org org, String label) {
        return ChannelFactory.lookupByLabel(org, label);
    }

    /**
     * Returns available entitlements for the org and the given channel.
     * @param org Org
     * @param c Channel
     * @return available entitlements for the org and the given channel.
     */
    public static Long getAvailableEntitlements(Org org, Channel c) {
        ChannelEntitlementCounter counter = (ChannelEntitlementCounter) MethodUtil
                .getClassFromConfig(ChannelEntitlementCounter.class.getName());

        Long retval = counter.getAvailableEntitlements(org, c);
        log.debug("getAvailableEntitlements: " + c.getLabel() + " got: " + retval);

        return retval;
    }

    /**
     * Returns available flex entitlements for the org and the given channel.
     * @param org Org
     * @param c Channel
     * @return available flex entitlements for the org and the given channel.
     */
    public static Long getAvailableFveEntitlements(Org org, Channel c) {
        ChannelEntitlementCounter counter = (ChannelEntitlementCounter) MethodUtil
                .getClassFromConfig(ChannelEntitlementCounter.class.getName());

        Long retval = counter.getAvailableFveEntitlements(org, c);
        log.debug("getAvailableFveEntitlements: " + c.getLabel() + " got: " + retval);

        return retval;
    }

    /**
     * Returns the latest packages in the channel. This call will return more details
     * about the channel than the API specific call
     * {@link #latestPackagesInChannel(com.redhat.rhn.domain.channel.Channel)}.
     *
     * @param channelId identifies the channel
     * @return list of packages in this channel
     */
    public static DataResult<PackageListItem> latestPackagesInChannel(Long channelId) {
        SelectMode m = ModeFactory.getMode("Package_queries", "latest_packages_in_channel");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", channelId);

        return m.execute(params);
    }

    /**
     * Returns list of latest packages in channel
     * @param channel channel whose packages are sought
     * @return list of latest packages in channel
     */
    public static List<Map<String, Object>> latestPackagesInChannel(Channel channel) {
        SelectMode m = ModeFactory.getMode("Package_queries", "latest_packages_in_channel_api");

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", channel.getId());

        return m.execute(params);
    }

    /**
     * List the errata applicable to a channel between start and end date
     * @param channel channel whose errata are sought
     * @param start start date
     * @param end end date
     * @param user the user doing the list
     * @return the errata applicable to a channel
     */
    public static DataResult<ErrataOverview> listErrata(Channel channel, Date start, Date end, User user) {
        String mode = "in_channel";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channel.getId());

        if (start != null) {
            params.put("start_date", new Timestamp(start.getTime()));
            mode = "in_channel_after";

            if (end != null) {
                params.put("end_date", new Timestamp(end.getTime()));
                mode = "in_channel_between";
            }
        }
        SelectMode m = ModeFactory.getMode("Errata_queries", mode);

        DataResult<ErrataOverview> dr = m.execute(params);
        Map<String, Object> elabParams = new HashMap<String, Object>();
        elabParams.put("user_id", user.getId());
        dr.setElaborationParams(elabParams);
        return dr;
    }

    /**
     * List the errata applicable to a channel (used for repomd generation)
     * @param channelId channel whose errata are sought
     * @return the errata applicable to a channel
     */
    public static DataResult<ErrataOverview> listErrataSimple(Long channelId) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channelId);
        SelectMode m = ModeFactory.getMode("Errata_queries", "simple_in_channel");

        DataResult<ErrataOverview> dr = m.execute(params);
        Map<String, Object> elabParams = new HashMap<String, Object>();
        dr.setElaborationParams(elabParams);
        return dr;
    }

    /**
     * List the errata applicable to a channel between start and end date
     * @deprecated Use appropriate listErrata
     * @param channel channel whose errata are sought
     * @param start start date
     * @param end end date
     * @return the errata applicable to a channel
     */
    @Deprecated
    public static DataResult<Map<String, Object>> listErrataForDates(Channel channel, String start, String end) {
        String mode = "relevant_to_channel_deprecated";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channel.getId());

        if (!StringUtils.isEmpty(start)) {
            params.put("start_date_str", start);
            mode = "relevant_to_channel_after_deprecated";

            if (!StringUtils.isEmpty(end)) {
                params.put("end_date_str", end);
                mode = "relevant_to_channel_between_deprecated";
            }
        }

        SelectMode m = ModeFactory.getMode("Errata_queries", mode);

        return m.execute(params);
    }

    /**
     * List the errata of a particular type that are applicable to a channel.
     * @param channel channel whose errata are sought
     * @param type type of errata
     * @return the errata applicable to a channel
     */
    public static DataResult<Map<String, Object>> listErrataByType(Channel channel, String type) {

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channel.getId());
        params.put("type", type);

        SelectMode m = ModeFactory.getMode("Errata_queries", "relevant_to_channel_by_type");

        return m.execute(params);
    }

    /**
     * Returns list of packages in channel
     * @param channel channel whose packages are sought
     * @param startDate package start date
     * @param endDate package end date
     * @return list of packages in channel
     */
    public static List<PackageDto> listAllPackages(Channel channel, String startDate, String endDate) {
        String mode = "all_packages_in_channel";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channel.getId());

        if (!StringUtils.isEmpty(startDate)) {
            params.put("start_date_str", startDate);
            mode = "all_packages_in_channel_after";
        }

        if (!StringUtils.isEmpty(endDate)) {
            params.put("end_date_str", endDate);
            mode = "all_packages_in_channel_between";
        }

        SelectMode m = ModeFactory.getMode("Package_queries", mode);

        return m.execute(params);
    }

    /**
     * Returns list of packages in channel
     * @param channel channel whose packages are sought
     * @return list of packages in channel
     */
    public static List<PackageDto> listAllPackages(Channel channel) {
        String mode = "all_packages_in_channel";
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", channel.getId());

        SelectMode m = ModeFactory.getMode("Package_queries", mode);

        return m.execute(params);
    }

    /**
     * Returns list of packages in channel
     * @param channel channel whose packages are sought
     * @param startDate package start date
     * @param endDate package end date
     * @return list of packages in channel
     */
    public static List<PackageDto> listAllPackages(Channel channel, Date startDate, Date endDate) {

        // convert the start and end dates to a string representation
        // that can be used in the db query...
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        String startDateStr = null;
        String endDateStr = null;

        if (startDate != null) {
            startDateStr = sdf.format(startDate);
        }
        if (endDate != null) {
            endDateStr = sdf.format(endDate);
        }

        return listAllPackages(channel, startDateStr, endDateStr);
    }

    /**
     * Returns list of packages in channel
     * @param channel channel whose packages are sought
     * @param startDate package start date
     * @param endDate package end date
     * @return list of packages in channel
     * @deprecated The only thing to use this is
     * ChannelSoftwareHandler.listAllPackagesByDate which is itself depricated
     */
    @Deprecated
    public static List<Map<String, Object>> listAllPackagesByDate(Channel channel, String startDate,
            String endDate) {

        String mode = "all_packages_in_channel_by_date";
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channel.getId());

        if (!StringUtils.isEmpty(startDate)) {
            params.put("start_date_str", startDate);
            mode = "all_packages_in_channel_after_by_date";
        }

        if (!StringUtils.isEmpty(endDate)) {
            params.put("end_date_str", endDate);
            mode = "all_packages_in_channel_between_by_date";
        }

        SelectMode m = ModeFactory.getMode("Package_queries", mode);

        return m.execute(params);
    }

    /**
     * Get the id of latest packages equal in the passed in Channel and name
     *
     * @param channelId to lookup package against
     * @param packageName to check
     * @return List containing Maps of "CP.package_id, CP.name_id, CP.evr_id"
     */
    public static Long getLatestPackageEqual(Long channelId, String packageName) {
        List<Map<String, Object>> latestPkgs = listLatestPackagesEqual(channelId, packageName);
        if (latestPkgs != null && latestPkgs.size() > 0) {
            return (Long) latestPkgs.get(0).get("package_id");
        }
        return null;
    }

    /**
     * Get the id of latest packages located in the channel tree where channelId
     * is a parent
     *
     * @param channelId to lookup package against
     * @param packageName to check
     * @return package id of the newest package with a matching name
     */
    public static Long getLatestPackageEqualInTree(Long channelId, String packageName) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "latest_package_equal_in_tree");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channelId);
        params.put("name", packageName);
        // Maps of "package_id, evr_id, arch_label"
        List<Map<String, Object>> results = m.execute(params);

        // See bug 1094364. If we wanted to really really fix this we would have
        // to add a channel-arch-to-default-package-arch mapping in the database
        // for every possible architecture. However that would still be somewhat
        // of a heuristic because we may want to install a non-default-arch package
        // at some point in the future. Much easier and almost just as good to
        // have a hueristic here that returns the package arch we probably want.
        if (results != null && results.size() > 0) {
            Map<String, Object> row = results.get(0);
            if (results.size() == 1) {
                return (Long) row.get("package_id");
            }
            // "default arches". If a channel contains multiple arches (eg.
            // "i386" and "x86_86") then these are probably the arches that we
            // want to install by default.
            List<String> defaultArches = new ArrayList<String>();
            defaultArches.add("x86_64");
            defaultArches.add("sparc64");
            defaultArches.add("s390x");
            defaultArches.add("armv7hnl");

            // more than one result. they are ordered based on EVR, so let's
            // examine the packages that have the same EVR as the first one and
            // see if we can find one that is of the default arch. If we run out
            // or go down to an older EVR, just return first result as a fallback.
            for (Map<String, Object> result : results) {
                if (!((Long) result.get("evr_id")).equals(row.get("evr_id"))) {
                    break;
                }
                if (defaultArches.contains(result.get("arch_label"))) {
                    return (Long) result.get("package_id");
                }
            }
            return (Long) row.get("package_id");
        }
        return null;
    }

    /**
     * List the latest packages equal in the passed in Channel and name
     *
     * @param channelId to lookup package against
     * @param packageName to check
     * @return List containing Maps of "CP.package_id, CP.name_id, CP.evr_id"
     */
    public static List<Map<String, Object>> listLatestPackagesEqual(Long channelId, String packageName) {
        if (log.isDebugEnabled()) {
            log.debug("listLatestPackagesEqual: " + channelId + " pn: " + packageName);
        }
        SelectMode m = ModeFactory.getMode("Channel_queries", "latest_package_equal");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channelId);
        params.put("name", packageName);
        return m.execute(params);

    }

    /**
     * List the latest packages in a channel *like* the given package name.
     *
     * @param channelId to lookup package against
     * @param packageName to check
     * @return List containing Maps of "CP.package_id, CP.name_id, CP.evr_id"
     */
    public static List<Map<String, Object>> listLatestPackagesLike(Long channelId, String packageName) {
        if (log.isDebugEnabled()) {
            log.debug("listLatestPackagesLike() cid: " + channelId + " packageName : " + packageName);
        }
        SelectMode m = ModeFactory.getMode("Channel_queries", "latest_package_like");
        StringBuilder pname = new StringBuilder();
        pname.append("%");
        pname.append(packageName);
        pname.append("%");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", channelId);
        params.put("name", pname.toString());
        return m.execute(params);

    }

    /**
     * Finds the id of a child channel with the given parent channel id that contains
     * a package with the given name.  Will only find one child channel even if there are
     * many that qualify.
     * @param org Organization of the current user.
     * @param parent The id of the parent channel
     * @param packageName The exact name of the package sought for.
     * @return The id of a single child channel found or null if nothing found.
     */
    public static Long findChildChannelWithPackage(Org org, Long parent, String packageName) {

        List<Long> cids = findChildChannelsWithPackage(org, parent, packageName, true);
        if (cids.isEmpty()) {
            return null;
        }
        return cids.get(0);
    }

    /**
     * Finds the id of a child channel with the given parent channel id that contains
     * a package with the given name.  Returns all child channel unless expectOne is True
     * @param org Organization of the current user.
     * @param parent The id of the parent channel
     * @param packageName The exact name of the package sought for.
     * @param expectOne if true, throws exception, if more child channels are returned
     * @return List of child channel ids
     */
    public static List<Long> findChildChannelsWithPackage(Org org, Long parent, String packageName,
            boolean expectOne) {

        SelectMode m = ModeFactory.getMode("Channel_queries", "channel_with_package");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("parent", parent);
        params.put("package", packageName);
        params.put("org_id", org.getId());

        DataResult dr = m.execute(params);
        List<Long> channelIds = new ArrayList<Long>();
        for (Iterator it = dr.iterator(); it.hasNext();) {
            channelIds.add((Long) ((Map) it.next()).get("id"));
        }
        if (expectOne && channelIds.size() > 1) {
            // Multiple channels have this package, highly unlikely we can guess which
            // one is the right one so we'll raise an exception and let the caller
            // decide what to do.
            throw new MultipleChannelsWithPackageException(channelIds);
        }

        return channelIds;
    }

    /**
     * Finds the ids of all child channels that contain
     * a package with the given name.  Will only all the child channels.
     * @param packageName The exact name of the package sought for.
     * @param org the org this is in
     * @return The list of ids
     */
    public static List<Long> findChildChannelsWithPackage(String packageName, Org org) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "child_channels_with_package");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("package", packageName);
        params.put("org_id", org.getId());
        //whittle down until we have the piece we want.
        DataResult<Map<String, Long>> dr = m.execute(params);
        List<Long> cids = new LinkedList<Long>();
        for (Map<String, Long> row : dr) {
            cids.add(row.get("id"));
        }
        return cids;
    }

    /**
     * Subscribe a Server to the first child channel of its base channel that contains
     * the packagename passed in.  Returns false if it can't be subscribed.
     *
     * @param user requesting the subscription
     * @param current System to be subbed
     * @param packageName to use to lookup the channel with.
     * @return Channel we subscribed to, null if not.
     */
    public static Channel subscribeToChildChannelWithPackageName(User user, Server current, String packageName) {

        log.debug("subscribeToChildChannelWithPackageName: " + current.getId() + " name: " + packageName);
        /*
         * First make sure that we have a base channel.
         * Second, make sure that the base channel has an RHN Tools child channel.
         * Third, try to subscribe to that child channel.
         */
        if (current.getBaseChannel() == null) {
            log.debug("base channel for server is null. returning null");
            return null;
        }

        // We know its the channel we want if it has the package in it:
        Long bcid = current.getBaseChannel().getId();
        log.debug("found basechannel: " + bcid);

        Long cid = null;

        try {
            cid = ChannelManager.findChildChannelWithPackage(user.getOrg(), bcid, packageName);
        } catch (MultipleChannelsWithPackageException e) {
            // If multiple channels have the package we're looking for, see if the server
            // already has access to one of them before raising this exception.
            for (Long channelId : e.getChannelIds()) {
                Channel c = ChannelManager.lookupByIdAndUser(channelId, user);
                if (current.isSubscribed(c)) {
                    // found a channel already subscribed
                    cid = channelId;
                    break;
                }
            }
            if (cid == null) {
                // Didn't find one, re-throw the exception:
                throw e;
            }
        }

        if (cid == null) { // Didnt find it ..
            log.debug("didnt find a child channel with the package.");
            return null;
        }

        Channel channel = null;
        try {
            channel = ChannelManager.lookupByIdAndUser(cid, user);
        } catch (LookupException e) {
            log.warn("User " + user.getLogin() + " does not have access to channel " + cid + ".");
        }

        boolean canSubscribe = false;

        // check to make sure we *can* sub to this channel
        if (channel != null) {
            canSubscribe = SystemManager.canServerSubscribeToChannel(user.getOrg(), current, channel);
        }
        if (!canSubscribe) {
            return null;
        }

        if (!current.isSubscribed(channel)) {
            if (log.isDebugEnabled()) {
                log.debug("Subscribing server to channel: " + channel);
            }
            SystemManager.subscribeServerToChannel(user, current, channel);
        }
        return channel;
    }

    /**
     * Subscribe a Server to the first child channel of its base channel supplies OS level
     * functionality based on the "osProductName" passed in.  In DB terms it is looking
     * for a child channel that has an entry in rhnDistChannel map with an OS field
     * matching the osProductName.
     *
     * @param user requesting the subscription
     * @param current System to be subbed
     * @param osProductName to use to lookup the channel with.
     * @return Channel we subscribed to, null if not.
     */
    public static Channel subscribeToChildChannelByOSProduct(User user, Server current, String osProductName) {

        /*
         * First make sure that we have a base channel.
         * Second, make sure that the base channel has an RHN Tools child channel.
         * Third, try to subscribe to that child channel.
         */
        if (current.getBaseChannel() == null) {
            log.debug("base channel for server is null. returning null");
            return null;
        }

        Channel baseChannel = current.getBaseChannel();
        Channel foundChannel = null;

        Iterator<Channel> i = ChannelManager.userAccessibleChildChannels(user.getOrg().getId(), baseChannel.getId())
                .iterator();
        while (i.hasNext()) {
            Channel child = i.next();
            Set<DistChannelMap> distChannelMaps = child.getDistChannelMaps();
            log.debug("distChannelMaps null? " + (distChannelMaps == null));
            if (distChannelMaps != null) {
                Iterator<DistChannelMap> di = distChannelMaps.iterator();
                while (di.hasNext()) {
                    DistChannelMap dcm = di.next();
                    log.debug("got DistChannelMap: " + dcm);
                    if (dcm.getOs().equals(osProductName)) {
                        log.debug("found a possible channel: " + dcm.getChannel());
                        foundChannel = dcm.getChannel();
                        if (SystemManager.canServerSubscribeToChannel(user.getOrg(), current, dcm.getChannel())) {
                            log.debug("we can subscribe.  lets set foundChannel");
                            foundChannel = dcm.getChannel();
                            break;
                        }
                        log.debug("no subscriptions available.");
                    }
                }
            }
        }

        if (foundChannel != null) {
            log.debug("we found a channel, now lets see if we should sub");
            if (!current.isSubscribed(foundChannel)) {
                if (log.isDebugEnabled()) {
                    log.debug("subChildChannelByOSProduct " + "Subscribing server to channel: " + foundChannel);
                }
                SystemManager.subscribeServerToChannel(user, current, foundChannel);
            }
        }
        log.debug("subscribeToChildChannelByOSProduct returning: " + foundChannel);
        return foundChannel;

    }

    /**
     * For the specified server, make a best-guess effort at what its base-channel
     * SHOULD be
     * @param usr User asking the question
     * @param s Server of interest
     * @return Channel that could serve as a base-channel for the Server
     */
    public static Channel guessServerBase(User usr, Server s) {
        Long guessedId = guessServerBase(usr, s.getId());

        Channel c = null;
        if (guessedId != null) {
            c = ChannelFactory.lookupByIdAndUser(guessedId, usr);
        }
        return c;
    }

    /**
     * For the specified server, make a best-guess effort at what its base-channel
     * SHOULD be
     * @param usr User asking the question
     * @param sid Server id of interest
     * @return Channel id
     */
    public static Long guessServerBase(User usr, Long sid) {
        // Figure out what this server's base OUGHT to be
        CallableMode sbm = ModeFactory.getCallableMode("Channel_queries", "guess_server_base");
        Map<String, Object> inParams = new HashMap<String, Object>();
        inParams.put("server_id", sid);
        Map<String, Integer> outParams = new HashMap<String, Integer>();
        outParams.put("result", new Integer(Types.NUMERIC));
        Map<String, Object> result = sbm.execute(inParams, outParams);

        return (Long) result.get(("result"));
    }

    /**
     * Convert redhat-release release values to those that are stored in the
     * rhnReleaseChannelMap table.
     *
     * RHEL 4 release samples: 7.6, 8, 9
     * RHEL 5 release samples: 5.1.0.1, 5.2.0.2, 5.3.0.3
     *
     * RHEL 4 must be treated specially, if the release is X.Y, we only wish to look at
     * the X portion.
     *
     * For RHEL 5 and presumably all future releases, we only look at the W.X.Y portion of
     * W.X.Y.Z.
     *
     * @param rhelVersion RHEL version we're comparing release for. (5Server, 4AS, 4ES)
     * @param originalRelease Original package release.
     * @return Release version for rhnReleaseChannelMap.
     */
    public static String normalizeRhelReleaseForMapping(String rhelVersion, String originalRelease) {

        String[] tokens = originalRelease.split("\\.");

        if (RHEL4_EUS_VERSIONS.contains(rhelVersion)) {
            if (tokens.length <= 1) {
                return originalRelease;
            }
            return tokens[0];
        }

        if (tokens.length <= 3) {
            return originalRelease;
        }

        StringBuilder buf = new StringBuilder();
        buf.append(tokens[0]);
        buf.append(".");
        buf.append(tokens[1]);
        buf.append(".");
        buf.append(tokens[2]);
        return buf.toString();
    }

    /**
     * Search for the tools channel beneath the specified base channel.
     * Queries for package names that look like kickstart packages, and
     * assumes that if any are found in this channel is must be the
     * tools channel.
     * @param baseChannel Base channel to search for a tools channel beneath.
     * @param user User performing the search.
     * @return Tools channel if found, null otherwise.
     */
    public static Channel getToolsChannel(Channel baseChannel, User user) {
        if (log.isDebugEnabled()) {
            log.debug("getToolsChannel, baseChannel: " + baseChannel.getLabel());
        }

        Iterator<Channel> i = ChannelManager.userAccessibleChildChannels(user.getOrg().getId(), baseChannel.getId())
                .iterator();

        if (log.isDebugEnabled()) {
            log.debug("getToolsChannel, userAccessibleChildChannels: " + i.hasNext());
        }
        while (i.hasNext()) {
            Channel child = i.next();
            if (log.isDebugEnabled()) {
                log.debug("getToolsChannel, trying: " + child.getLabel());
            }
            // First search for legacy kickstart package names:
            List<Map<String, Object>> kspackages = ChannelManager.listLatestPackagesLike(child.getId(),
                    KickstartData.LEGACY_KICKSTART_PACKAGE_NAME);
            if (kspackages.size() > 0) {
                return child;
            }

            // Search for rhn-kickstart package name:
            kspackages = ChannelManager.listLatestPackagesEqual(child.getId(),
                    ConfigDefaults.get().getKickstartPackageName());
            if (kspackages.size() > 0) {
                return child;
            }
        }
        return null;
    }

    /**
     * Examines each DistChannelMap associated with the given channel to build the
     * master set of all supported channel version constants.
     * @param channel Channel to return the versions for.
     * @return Set of all supported channel versions.
     */
    public static Set<ChannelVersion> getChannelVersions(Channel channel) {
        Set<ChannelVersion> returnSet = new HashSet<ChannelVersion>();
        Iterator<DistChannelMap> iter = channel.getDistChannelMaps().iterator();
        while (iter.hasNext()) {
            DistChannelMap dcm = iter.next();

            returnSet.add(ChannelVersion.getChannelVersionForDistChannelMap(dcm));
        }
        return returnSet;
    }

    /**
     * Returns all channels that are applicable to the systems currently selected in
     * the SSM.
     *
     * @param user logged in user
     * @param lc   controller for the UI list
     * @return description of all channels applicable to the systems
     */
    public static DataResult<ChannelTreeNode> getChannelsForSsm(User user, ListControl lc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channel_tree_ssm_install");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("org_id", user.getOrg().getId());
        params.put("user_id", user.getId());
        params.put("set_label", RhnSetDecl.SYSTEMS.getLabel());
        DataResult dr = makeDataResult(params, params, lc, m);

        return dr;
    }

    /**
     * Returns the list of all child-channels in the user's System Set
     * @param user User whose channels are sought.
     * @return the list of all child-channels in that user's System Set
     */
    public static DataResult<ChildChannelDto> childrenAvailableToSet(User user) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "children_in_set");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("user_id", user.getId());

        return m.execute(params);
    }

    /**
     * Returns the list of all base-channels represented in the System Set.
     * @param user User whose System Set is being considered
     * @return the list of all base-channels in that set.
     */
    public static DataResult<SystemsPerChannelDto> baseChannelsInSet(User user) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "base_channels_in_set");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("user_id", user.getId());

        return m.execute(params);
    }

    private static boolean isDefaultBaseChannel(Channel baseChan, String version) {
        if (baseChan == null) {
            return false;
        }

        Channel defaultBaseChan = getDefaultBaseChannel(version, baseChan.getChannelArch());
        if (defaultBaseChan == null) {
            return false;
        }
        return defaultBaseChan.getId().equals(baseChan.getId());
    }

    private static Channel getDefaultBaseChannel(String version, ChannelArch arch) {
        DistChannelMap dcm = ChannelManager.lookupDistChannelMapByPnReleaseArch(RHEL_PRODUCT_NAME, version, arch);
        if (dcm == null) {
            return null;
        }
        return dcm.getChannel();
    }

    /**
     * Given a system, find all the base channels available to the specified user
     * that the system may be re-subscribed to.
     *
     * If the system is currently subscribed to the default RHEL channel for their
     * version (i.e. main RHEL 4), then only the *newest* available EUS channel
     * will be included.
     *
     * If the system is already subscribed to a non-default channel (i.e. EUS or custom),
     * we look up the redhat-release package information on the system and use it's
     * version/release information to search for all *newer* EUS channels.
     *
     * Custom base channels for this organization will always be returned,
     * regardless of the RHEL version.
     *
     * @param usr requesting list
     * @param s Server to check against
     * @return List of Channel objects that match
     */
    public static List<EssentialChannelDto> listBaseChannelsForSystem(User usr, Server s) {

        List<EssentialChannelDto> channelDtos = new LinkedList<EssentialChannelDto>();
        PackageEvr releaseEvr = PackageManager.lookupReleasePackageEvrFor(s);
        if (releaseEvr != null) {
            String rhelVersion = releaseEvr.getVersion();

            List<EssentialChannelDto> baseEusChans = new LinkedList<EssentialChannelDto>();
            if (isDefaultBaseChannel(s.getBaseChannel(), rhelVersion)) {
                EssentialChannelDto baseEus = lookupLatestEusChannelForRhelVersion(usr, rhelVersion,
                        s.getBaseChannel().getChannelArch().getId());
                if (baseEus != null) {
                    baseEusChans.add(baseEus);
                }
            } else {
                Channel currBase = s.getBaseChannel();
                if (currBase != null) {
                    ReleaseChannelMap rcm = lookupDefaultReleaseChannelMapForChannel(currBase);
                    if (rcm != null) {
                        baseEusChans = listBaseEusChannelsByVersionReleaseAndServerArch(usr, rhelVersion,
                                releaseEvr.getRelease(), s.getServerArch().getLabel());
                    }
                }
            }
            channelDtos.addAll(baseEusChans);
        }

        // Get all the possible base-channels owned by this Org
        channelDtos.addAll(listCustomBaseChannelsForServer(s));

        for (DistChannelMap dcm : ChannelFactory.listCompatibleDcmByServerInNullOrg(s)) {
            if (SystemManager.canServerSubscribeToChannel(usr.getOrg(), s, dcm.getChannel())) {
                channelDtos.add(new EssentialChannelDto(dcm.getChannel()));
            }
        }

        return channelDtos;
    }

    /**
     * Given a base-channel, find all the base channels available to the specified user
     * that a system with the specified channel may be re-subscribed to.
     *
     * @param u User of interest
     * @param inChan Base-channel of interest
     * @return List of channels that a system subscribed to "c" could be re-subscribed to
     */
    public static List<EssentialChannelDto> listCompatibleBaseChannelsForChannel(User u, Channel inChan) {

        List<EssentialChannelDto> retval = new ArrayList<EssentialChannelDto>();

        // Get all the custom-channels owned by this org and add them
        for (Channel c : ChannelFactory.listCustomBaseChannelsForSSM(u, inChan)) {
            retval.add(new EssentialChannelDto(c));
        }

        for (Channel c : ChannelFactory.listCompatibleDcmForChannelSSMInNullOrg(u, inChan)) {
            retval.add(new EssentialChannelDto(c));
        }

        List<EssentialChannelDto> eusBaseChans = new LinkedList<EssentialChannelDto>();

        ReleaseChannelMap rcm = lookupDefaultReleaseChannelMapForChannel(inChan);
        if (rcm != null) {
            eusBaseChans.addAll(listBaseEusChannelsByVersionReleaseAndChannelArch(u, rcm.getVersion(),
                    rcm.getRelease(), inChan.getChannelArch().getId()));
        } else {
            for (DistChannelMap dcm : inChan.getDistChannelMaps()) {
                String rhelVersion = dcm.getRelease();
                if (isDefaultBaseChannel(inChan, rhelVersion)) {
                    EssentialChannelDto latestEus = lookupLatestEusChannelForRhelVersion(u, rhelVersion,
                            inChan.getChannelArch().getId());
                    if (latestEus != null) {
                        eusBaseChans.add(latestEus);
                    }
                }
            }
        }
        retval.addAll(eusBaseChans);

        for (EssentialChannelDto dto : retval) {
            if (dto.getId().longValue() == inChan.getId().longValue()) {
                retval.remove(dto); // normally not a good idea, but we do break
                break;
            }
        }

        return retval;
    }

    /**
     * Lookup the default release channel map for the given channel. Returns null if no
     * default is found.
     *
     * @param channel Channel to lookup mapping for
     * @return Default ReleaseChannelMap
     */
    public static ReleaseChannelMap lookupDefaultReleaseChannelMapForChannel(Channel channel) {
        return ChannelFactory.lookupDefaultReleaseChannelMapForChannel(channel);
    }

    /**
     * Lookup the dist channel map for the given os, release, and channel arch.
     * Returns null if none is found.
     *
     * @param productName Product name.
     * @param release Version.
     * @param channelArch Channel arch.
     * @return DistChannelMap, null if none is found
     */
    public static DistChannelMap lookupDistChannelMapByPnReleaseArch(String productName, String release,
            ChannelArch channelArch) {
        return ChannelFactory.lookupDistChannelMapByPnReleaseArch(productName, release, channelArch);
    }

    /**
     * Lookup the EUS base channels suitable for the given version, release, and
     * server arch.
     *
     * NOTE: Release not actually used in the database query, must filter manually
     * in application code due to some very specific requirements on how it must
     * be compared. See normalizeRhelReleaseForMapping for more info.
     *
     * @param user User performing the query.
     * @param version RHEL version.
     * @param release RHEL version release.
     * @param serverArch RHEL server arch.
     * @return List of EssentialChannelDto's.
     */
    public static List<EssentialChannelDto> listBaseEusChannelsByVersionReleaseAndServerArch(User user,
            String version, String release, String serverArch) {

        log.debug("listBaseEusChannelsByVersionReleaseAndServerArch()");
        log.debug("   version = " + version);
        log.debug("   release = " + release);
        log.debug("   serverArch = " + serverArch);
        SelectMode m = ModeFactory.getMode("Channel_queries", "base_eus_channels_by_version_release_server_arch");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("user_id", user.getId());
        params.put("org_id", user.getOrg().getId());
        params.put("product_name_label", RHEL_PRODUCT_NAME);
        params.put("version", version);
        params.put("server_arch", serverArch);
        DataResult<EssentialChannelDto> dr = makeDataResult(params, new HashMap<String, Object>(), null, m,
                EssentialChannelDto.class);

        List<EssentialChannelDto> result = new LinkedList<EssentialChannelDto>();
        EusReleaseComparator comparator = new EusReleaseComparator(version);
        for (EssentialChannelDto dto : dr) {
            log.debug(dto.getId());
            if (comparator.compare(dto.getRelease(), release) >= 0) {
                result.add(dto);
            }
        }

        return result;
    }

    /**
     * Lookup the EUS base channels suitable for the given version, release, and
     * channel arch.
     *
     * NOTE: Release not actually used in the database query, must filter manually
     * in application code due to some very specific requirements on how it must
     * be compared. See normalizeRhelReleaseForMapping for more info.
     *
     * @param user User performing the query.
     * @param version RHEL version.
     * @param release RHEL release.
     * @param channelArchId Channel arch.
     * @return List of EssentialChannelDto's.
     */
    public static List<EssentialChannelDto> listBaseEusChannelsByVersionReleaseAndChannelArch(User user,
            String version, String release, Long channelArchId) {

        log.debug("listBaseEusChannelsByVersionReleaseAndChannelArch()");
        SelectMode m = ModeFactory.getMode("Channel_queries", "base_eus_channels_by_version_channel_arch");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("user_id", user.getId());
        params.put("org_id", user.getOrg().getId());
        log.debug("   version = " + version);
        log.debug("   release = " + release);
        log.debug("   channelArch = " + channelArchId);
        params.put("product_name_label", RHEL_PRODUCT_NAME);
        params.put("version", version);
        params.put("channel_arch_id", channelArchId);
        DataResult<EssentialChannelDto> dr = makeDataResult(params, new HashMap<String, Object>(), null, m,
                EssentialChannelDto.class);

        List<EssentialChannelDto> result = new LinkedList<EssentialChannelDto>();
        EusReleaseComparator comparator = new EusReleaseComparator(version);
        for (EssentialChannelDto dto : dr) {
            if (comparator.compare(dto.getRelease(), release) > 0) {
                result.add(dto);
            }
        }

        return result;
    }

    /**
     * Lookup the latest EUS base channel for the given version and server arch.
     *
     * Sorts based on the release column. null will be returned if none found.
     *
     * @param user User performing the query.
     * @param rhelVersion RHEL version.
     * @param channelArchId Channel arch id.
     * @return EssentialChannelDto, or null if no entry is found.
     */
    public static EssentialChannelDto lookupLatestEusChannelForRhelVersion(User user, String rhelVersion,
            Long channelArchId) {

        log.debug("listBaseEusChannelsByVersionAndChannelArch");
        SelectMode m = ModeFactory.getMode("Channel_queries", "base_eus_channels_by_version_channel_arch");

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("user_id", user.getId());
        params.put("org_id", user.getOrg().getId());
        log.debug("   version = " + rhelVersion);
        log.debug("   channelArch = " + channelArchId);
        params.put("product_name_label", RHEL_PRODUCT_NAME);
        params.put("version", rhelVersion);
        params.put("channel_arch_id", channelArchId);
        DataResult<EssentialChannelDto> dr = makeDataResult(params, new HashMap<String, Object>(), null, m,
                EssentialChannelDto.class);
        if (dr.size() == 0) {
            return null;
        }
        Collections.sort(dr, new EusReleaseComparator(rhelVersion));
        return dr.get(dr.size() - 1);
    }

    /**
     * List base channels offered for the given server
     * @param server server
     * @return List of channels.
     */
    public static DataResult<EssentialChannelDto> listCustomBaseChannelsForServer(Server server) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "custom_base_channels_for_server");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("org_id", server.getOrg().getId());
        params.put("server_arch_id", server.getServerArch().getId());
        return makeDataResult(params, new HashMap<String, Long>(), null, m, EssentialChannelDto.class);
    }

    /**
     * List base channels (including Red Hat channels) for a given org.
     * @param u User to list base channels for.
     * @return List of Channels
     */
    public static List<Channel> findAllBaseChannelsForOrg(User u) {
        return ChannelFactory.listAllBaseChannels(u);
    }

    /**
     * Given an old and a new base channel, return a map of old child channels to new
     * child channels with the same product name. If no match can be found the old
     * child channel is omitted from the map.
     *
     * @param oldBaseChannel the base channel to which we need to find the equivalent
     *      child channels
     * @param newBaseChannel the base channel which holds the a child channel with
     *      same product name
     * @param user user needed for authentication purposes
     * @return a map [childChannel1:childChannel2]
     */
    public static Map<Channel, Channel> findCompatibleChildren(Channel oldBaseChannel, Channel newBaseChannel,
            User user) {

        Map<Channel, Channel> compatibleChannels = new HashMap<Channel, Channel>();
        if (oldBaseChannel == null) {
            return compatibleChannels;
        }
        if (oldBaseChannel.equals(newBaseChannel)) {
            Map<Channel, Channel> result = new HashMap<Channel, Channel>();
            for (Channel channel : oldBaseChannel.getAccessibleChildrenFor(user)) {
                result.put(channel, channel);
            }
            return result;
        }

        Map<ProductName, Channel> prodChannels = new HashMap<ProductName, Channel>();
        Set<ProductName> nonUniqueProducts = new HashSet<ProductName>();
        List<Channel> newChildren = newBaseChannel.getAccessibleChildrenFor(user);
        for (Channel channel : newChildren) {
            if (channel.getProductName() != null) {
                if (prodChannels.get(channel.getProductName()) != null) {
                    nonUniqueProducts.add(channel.getProductName());
                }
                prodChannels.put(channel.getProductName(), channel);
            }
        }

        Map<Channel, List<Channel>> originalToClones = getOrignalToClonesMap(newChildren);

        for (Channel childOne : oldBaseChannel.getAccessibleChildrenFor(user)) {
            // if a new child was cloned from the same original as an old one,
            // return them as compatible
            List<Channel> candidates = originalToClones.get(getOriginalChannel(childOne));
            if (candidates != null && candidates.size() == 1) {
                compatibleChannels.put(childOne, candidates.get(0));
            } else {
                // if a new child refers to the same product name as an old one,
                // return them as compatible
                ProductName name = childOne.getProductName();
                if (prodChannels.containsKey(name) && !nonUniqueProducts.contains(name)) {
                    compatibleChannels.put(childOne, prodChannels.get(name));
                }
            }
        }

        return compatibleChannels;
    }

    /**
     * Returns a map in which values are channels specified by the argument and
     * keys are the corresponding original (ie. non-cloned) channels.
     * @param channels the channels to map
     * @return a map from originals to (possibly multiple) clones
     */
    private static Map<Channel, List<Channel>> getOrignalToClonesMap(List<Channel> channels) {
        Map<Channel, List<Channel>> result = new HashMap<Channel, List<Channel>>();
        for (Channel channel : channels) {
            Channel original = getOriginalChannel(channel);
            if (result.containsKey(original)) {
                result.get(original).add(channel);
            } else {
                List<Channel> entry = new LinkedList<Channel>();
                entry.add(channel);
                result.put(original, entry);
            }
        }
        return result;
    }

    /**
     * For a given {@link Channel}, determine the original {@link Channel}.
     *
     * @param channel channel
     * @return original channel
     */
    public static Channel getOriginalChannel(Channel channel) {
        while (channel.isCloned()) {
            channel = channel.getOriginal();
        }
        return channel;
    }

    /**
     * Finds non-custom errata for a target channel
     * @param targetChannel the channel to search for
     * @param packageAssoc  whether to filter packages on what packages are already
     *                      in the channel
     * @return List of errata
     */
    public static DataResult<ErrataOverview> findErrataForTarget(Channel targetChannel, boolean packageAssoc) {
        String mode;
        if (packageAssoc) {
            mode = "for_target_package_assoc";
        } else {
            mode = "for_target";
        }

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("custom_cid", targetChannel.getId());

        SelectMode m = ModeFactory.getMode("Errata_queries", mode);

        return m.execute(params);
    }

    /**
     * Finds errata associated with channels in the "channels_for_errata" rhnSet that
     *              apply to a custom channel
     * @param targetChannel the channel to search for
     * @param user the user doing the query
     * @param packageAssoc whether to filter packages on what packages are already
     *                      in the channel
     * @return List of Errata
     */
    public static DataResult<ErrataOverview> findErrataFromRhnSetForTarget(Channel targetChannel,
            boolean packageAssoc, User user) {

        String mode;
        if (packageAssoc) {
            mode = "in_sources_for_target_package_assoc";
        } else {
            mode = "in_sources_for_target";
        }

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("custom_cid", targetChannel.getId());
        params.put("user_id", user.getId());

        SelectMode m = ModeFactory.getMode("Errata_queries", mode);

        return m.execute(params);
    }

    /**
     * find available errata from custom base channels and their child channels for a
     *          particular channel
     * @param targetChannel the channel to target
     * @param packageAssoc whether to filter packages on what packages are already
     *                      in the channel
     *
     * @return List of errata
     */
    public static DataResult<ErrataOverview> findCustomErrataForTarget(Channel targetChannel,
            boolean packageAssoc) {
        String mode;
        if (packageAssoc) {
            mode = "custom_for_target_package_assoc";
        } else {
            mode = "custom_for_target";
        }

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("custom_cid", targetChannel.getId());
        params.put("org_id", targetChannel.getOrg().getId());

        SelectMode m = ModeFactory.getMode("Errata_queries", mode);

        return m.execute(params);
    }

    /**
     * Returns a list of compatible packages arches for the given ChannelArch
     * labels. It will NOT tell you which package arch goes with which channel
     * arch, for that you need to load the ChannelArch individually. This
     * methods purpose is for searching packages where we don't actually
     * care what package arch goes with what channel arch we only care what
     * package arch we should look for.
     * @param channelArchLabels List of ChannelArch labels.
     * @return list of compatible package arches.
     */
    public static List<String> listCompatiblePackageArches(String[] channelArchLabels) {
        if (channelArchLabels == null || (channelArchLabels.length < 1)) {
            return new ArrayList<String>();
        }

        SelectMode mode = ModeFactory.getMode("Package_queries", "compatible_package_arches");
        List<Map<String, String>> dr = mode.execute(Arrays.asList(channelArchLabels));
        List<String> result = new ArrayList<String>();
        for (Map<String, String> m : dr) {
            result.add(m.get("label"));
        }
        return result;
    }

    /**
     * Returns a distinct list of ChannelArch labels for all synch'd and custom
     * channels in the satellite.
     * @return a distinct list of ChannelArch labels for all synch'd and custom
     * channels in the satellite.
     */
    public static List<String> getSyncdChannelArches() {
        return ChannelFactory.findChannelArchLabelsSyncdChannels();
    }

    /**
     *
     * @param channelLabel channel label
     * @param client client info
     * @param reason reason for queue
     */
    public static void queueChannelChange(String channelLabel, String client, String reason) {
        if ("".equals(client)) {
            client = null;
        }

        if ("".equals(reason)) {
            reason = null;
        }

        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "request_repo_regen");
        Map<String, String> params = new HashMap<String, String>();
        params.put("label", channelLabel);
        params.put("client", client);
        params.put("reason", reason);
        m.executeUpdate(params);
    }

    /**
     * Remove packages from a channel very quickly
     * @param chan the channel
     * @param packageIds list of package ids
     * @param user the user doing the removing
     */
    public static void removePackages(Channel chan, List<Long> packageIds, User user) {

        if (!UserManager.verifyChannelAdmin(user, chan)) {
            StringBuilder msg = new StringBuilder("User: ");
            msg.append(user.getLogin());
            msg.append(" does not have channel admin access to channel: ");
            msg.append(chan.getLabel());

            LocalizationService ls = LocalizationService.getInstance();
            PermissionException pex = new PermissionException(msg.toString());
            pex.setLocalizedTitle(ls.getMessage("permission.jsp.title.channel"));
            pex.setLocalizedSummary(ls.getMessage("permission.jsp.summary.channel"));
            throw pex;
        }

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", chan.getId());

        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "remove_packages");
        m.executeUpdate(params, packageIds);

        HibernateFactory.getSession().refresh(chan);

    }

    /**
     * Adds packages to a channel
     * @param chan the channel
     * @param packageIds list of package ids
     * @param user the user adding packages
     */
    public static void addPackages(Channel chan, List<Long> packageIds, User user) {

        if (!UserManager.verifyChannelAdmin(user, chan)) {
            StringBuilder msg = new StringBuilder("User: ");
            msg.append(user.getLogin());
            msg.append(" does not have channel admin access to channel: ");
            msg.append(chan.getLabel());

            LocalizationService ls = LocalizationService.getInstance();
            PermissionException pex = new PermissionException(msg.toString());
            pex.setLocalizedTitle(ls.getMessage("permission.jsp.title.channel"));
            pex.setLocalizedSummary(ls.getMessage("permission.jsp.summary.channel"));
            throw pex;
        }

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", chan.getId());

        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "add_channel_packages");
        m.executeUpdate(params, packageIds);

        HibernateFactory.getSession().refresh(chan);
    }

    /**
     * Remove a set of erratas from a channel
     *      and remove associated packages
     * @param chan The channel to remove from
     * @param errataIds set of errata ids to remove
     * @param user the user doing the removing
     */
    public static void removeErrata(Channel chan, Set<Long> errataIds, User user) {
        if (!UserManager.verifyChannelAdmin(user, chan)) {
            throw new PermissionException(RoleFactory.CHANNEL_ADMIN);
        }

        List<Long> ids = new ArrayList<Long>();
        ids.addAll(errataIds);

        List<Long> pids = ChannelFactory.getChannelPackageWithErrata(chan, ids);

        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", chan.getId());

        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "remove_errata");
        m.executeUpdate(params, ids);

        m = ModeFactory.getWriteMode("Channel_queries", "remove_errata_packages");
        m.executeUpdate(params, ids);

        ChannelManager.refreshWithNewestPackages(chan, "Remove errata");
        ErrataCacheManager.deleteCacheEntriesForChannelPackages(chan.getId(), pids);
        ErrataCacheManager.deleteCacheEntriesForChannelErrata(chan.getId(), ids);
        ChannelFactory.getSession().refresh(chan);
    }

    /**
     * List packages that are contained in an errata and in a channel
     * @param chan The channel
     * @param errata the Errata
     * @return A list of PackageDto that are in the channel and errata
     */
    public static List<PackageDto> listErrataPackages(Channel chan, Errata errata) {
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", chan.getId());
        params.put("eid", errata.getId());

        SelectMode mode = ModeFactory.getMode("Channel_queries", "channel_errata_packages");
        return mode.execute(params);
    }

    /**
     * Returns an id of the original channel
     * @param channel The cloned channel
     * @return A original channel id
     */
    public static Long lookupOriginalId(Channel channel) {
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("cid", channel.getId());

        SelectMode mode = ModeFactory.getMode("Channel_queries", "cloned_original_id");
        List<Map> list = mode.execute(params);
        if (!list.isEmpty()) {
            Map map = list.get(0);
            return (Long) map.get("id");
        }
        return null;
    }

    /**
     * List errata that is within a channel that needs to be resynced
     *  This is determined by the packages in the channel
     *
     * @param c the channel
     * @param user the user
     * @return list of errataOverview objects that need to be resynced
     */
    public static List<ErrataOverview> listErrataNeedingResync(Channel c, User user) {
        if (!user.hasRole(RoleFactory.CHANNEL_ADMIN)) {
            throw new PermissionException(RoleFactory.CHANNEL_ADMIN);
        }

        if (c.isCloned()) {
            Map<String, Long> params = new HashMap<String, Long>();
            params.put("cid", c.getId());
            params.put("ocid", c.getOriginal().getId());
            SelectMode m = ModeFactory.getMode("Errata_queries", "list_errata_needing_sync");
            return m.execute(params);
        }
        return new ArrayList<ErrataOverview>();
    }

    /**
     * List errata packages that need to be resynced
     * @param c the channel to look for packages in
     * @param user the user doing it
     * @return the list of PackageOverview objects
     */
    public static List<PackageOverview> listErrataPackagesForResync(Channel c, User user) {
        if (!user.hasRole(RoleFactory.CHANNEL_ADMIN)) {
            throw new PermissionException(RoleFactory.CHANNEL_ADMIN);
        }

        if (c.isCloned()) {
            Map<String, Long> params = new HashMap<String, Long>();
            params.put("cid", c.getId());
            params.put("ocid", c.getOriginal().getId());
            SelectMode m = ModeFactory.getMode("Errata_queries", "list_packages_needing_sync");
            return m.execute(params);
        }
        return new ArrayList<PackageOverview>();
    }

    /**
     * List errata packages that need to be resynced
     * @param c the channel to look for packages in
     * @param user the user doing it
     * @param setLabel the set of errata to base the package off of
     * @return the list of PackageOverview objects
     */
    public static List<PackageOverview> listErrataPackagesForResync(Channel c, User user, String setLabel) {
        if (!user.hasRole(RoleFactory.CHANNEL_ADMIN)) {
            throw new PermissionException(RoleFactory.CHANNEL_ADMIN);
        }

        if (c.isCloned()) {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("cid", c.getId());
            params.put("set_label", setLabel);
            params.put("ocid", c.getOriginal().getId());
            SelectMode m = ModeFactory.getMode("Errata_queries", "list_packages_needing_sync_from_set");
            return m.execute(params);
        }
        return new ArrayList<PackageOverview>();
    }

    /**
     * Check the status of the cache repo data
     * @param channel the channel to look for status
     * @return repodata status
     */
    public static boolean isChannelLabelInProgress(String channel) {
        SelectMode selector = ModeFactory.getMode(TaskConstants.MODE_NAME,
                TaskConstants.TASK_QUERY_REPOMD_DETAILS_QUERY);
        Map<Object, Object> params = new HashMap<Object, Object>();
        params.put("channel_label", channel);
        return (selector.execute(params).size() > 0);
    }

    /**
     * get the last build date on repodata per channel
     * @param channel the channel to look for repodata build date
     * @return last repo build date
     */
    public static String getRepoLastBuild(Channel channel) {
        String pathPrefix = Config.get().getString(ConfigDefaults.REPOMD_PATH_PREFIX, "rhn/repodata");
        String mountPoint = Config.get().getString(ConfigDefaults.REPOMD_CACHE_MOUNT_POINT, "/pub");
        File theFile = new File(mountPoint + File.separator + pathPrefix + File.separator + channel.getLabel()
                + File.separator + "repomd.xml");
        if (!theFile.exists()) {
            // No repo file, dont bother computing build date
            return null;
        }
        Date fileModifiedDateIn = new Date(theFile.lastModified());
        // the file Modified date should be getting set when the file
        // is moved into the correct location.
        log.info("File Modified Date:" + fileModifiedDateIn);
        return LocalizationService.getInstance().formatCustomDate(fileModifiedDateIn);
    }

    /**
     * get the latest log file for spacewalk-repo-sync
     * @param c channel
     * @return the string of the filename (fully qualified)
     */
    public static List<String> getLatestSyncLogFiles(Channel c) {

        String logPath = Config.get().getString(ConfigDefaults.SPACEWALK_REPOSYNC_LOG_PATH,
                "/var/log/rhn/reposync/");

        File dir = new File(logPath);
        List<String> possibleList = new ArrayList<String>();
        String[] dirList = dir.list();
        if (dirList != null) {
            for (String file : dirList) {
                if (file.startsWith(c.getLabel()) && !file.endsWith(".gz")) {
                    possibleList.add(logPath + file);
                }
            }
            Collections.sort(possibleList);
        }
        return possibleList;
    }

    /**
     * Takes a list of child channels and a set label and returns a Map
     *    with system ids as the key and a set ChannelActionDAO's
     *     as the value
     * @param setLabel the set label of System ids
     * @param subChans the list of channels to subscribe
     * @param unsubChans the list of channels to unsubscribe
     * @param user the user doing the work
     * @return The aformentioned map
     */
    public static Map<Long, ChannelActionDAO> filterChildSubscriptions(String setLabel, List<Channel> subChans,
            List<Channel> unsubChans, User user) {
        Map<Long, ChannelActionDAO> toRet = new HashMap<Long, ChannelActionDAO>();

        List<Long> subCids = new ArrayList<Long>();
        for (Channel c : subChans) {
            subCids.add(c.getId());
        }
        List<Long> unsubCids = new ArrayList<Long>();
        for (Channel c : unsubChans) {
            unsubCids.add(c.getId());
        }

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("uid", user.getId());
        params.put("set_label", setLabel);

        SelectMode m = null;
        List<Map<String, Object>> subDr = new ArrayList<Map<String, Object>>();
        List<Map<String, Object>> unsubDr = new ArrayList<Map<String, Object>>();
        if (!subChans.isEmpty()) {
            m = ModeFactory.getMode("Channel_queries", "ssm_systems_for_child_subscription");
            subDr = m.execute(params, subCids);
        }
        if (!unsubChans.isEmpty()) {
            m = ModeFactory.getMode("Channel_queries", "ssm_systems_for_child_unsubscription");
            unsubDr = m.execute(params, unsubCids);
        }

        for (Map<String, Object> row : subDr) {
            Long id = (Long) row.get("id");
            ChannelActionDAO sys = toRet.get(id);
            if (sys == null) {
                sys = new ChannelActionDAO();
                sys.setId(id);
                sys.setName((String) row.get("name"));
                toRet.put(id, sys);
            }
            sys.addSubscribeChannelId((Long) row.get("channel_id"));
            sys.addSubscribeName((String) row.get("channel_name"));
        }

        for (Map<String, Object> row : unsubDr) {
            Long id = (Long) row.get("id");
            ChannelActionDAO sys = toRet.get(id);
            if (sys == null) {
                sys = new ChannelActionDAO();
                sys.setId(id);
                sys.setName((String) row.get("name"));
                toRet.put(id, sys);
            }
            sys.addUnsubscribeChannelId((Long) row.get("channel_id"));
            sys.addUnsubcribeName((String) row.get("channel_name"));
        }

        return toRet;
    }

    /**
     * returns channel manager ids within the given org for a given channel
     * @param org given organization
     * @param channel channel
     * @return list of channel manager ids
     */
    public static List<Long> listChannelManagerIdsForChannel(Org org, Channel channel) {
        return ChannelFactory.listManagerIdsForChannel(org, channel.getId());
    }

    /**
     * returns channel subscriber ids within the given org for a given channel
     * @param org given organization
     * @param channel channel
     * @return list of channel subscriber ids
     */
    public static List<Long> listChannelSubscriberIdsForChannel(Org org, Channel channel) {
        return ChannelFactory.listSubscriberIdsForChannel(org, channel.getId());
    }

    /**
     * @param csid content source (repository) ID
     * @param pc pageControl
     * @return List of channels associated to a content source (repository)
     */
    public static DataResult channelsForContentSource(Long csid, PageControl pc) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "channels_for_content_source");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("csid", csid);
        Map<String, Object> elabParams = new HashMap<String, Object>();
        return makeDataResult(params, elabParams, pc, m);
    }

    /**
     * @param parentArchLabel The channel arch label of the parent channel
     * @return List of {'name': channel_arch_name, 'label': channel_arch_label}
     *   for compatible child channel arches.
     */
    public static List<Map<String, String>> compatibleChildChannelArches(String parentArchLabel) {
        SelectMode m = ModeFactory.getMode("Channel_queries", "compatible_child_channel_arches");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("pa_label", parentArchLabel);
        return m.execute(params);
    }

    /**
     * Clone the original channel packages from one channel to another
     * @param fromCid The original channel's id
     * @param toCid The cloned channel's id
     * @return 1 if successfull
     */
    public static int cloneOriginalChannelPackages(Long fromCid, Long toCid) {
        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "clone_original_channel_packages");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("from", fromCid);
        params.put("to", toCid);
        return m.executeUpdate(params);
    }

    /**
     * Clone all channel packages from one channel to another
     * @param fromCid The original channel's id
     * @param toCid The cloned channel's id
     * @return 1 if successfull
     */
    public static int cloneChannelPackages(Long fromCid, Long toCid) {
        WriteMode m = ModeFactory.getWriteMode("Channel_queries", "clone_channel_packages");
        Map<String, Long> params = new HashMap<String, Long>();
        params.put("from", fromCid);
        params.put("to", toCid);
        return m.executeUpdate(params);
    }

    /**
     * Return the channel id of the "most likely" parent if we're cloning this
     * channel. "Most likely" is determined by:
     *   1) See if the org owns a clone of the original channel's parent
     *     1.a) if multiple choose most recently modified
     *   2) Else return the original channel's parent id
     * Returns null if original is not a child channel
     * @param original Original channel that we are cloning
     * @param org Org to look for clones in
     * @return channel id of most likely parent
     */
    public static Long likelyParentId(Channel original, Org org) {
        if (original.isBaseChannel()) {
            return null;
        }
        SelectMode m = ModeFactory.getMode("Channel_queries", "likely_parent");
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("cid", original.getId());
        params.put("org_id", org.getId());
        List<Map<String, Object>> result = m.execute(params);

        if (result.size() != 0) {
            return (Long) result.get(0).get("id");
        }

        return original.getParentChannel().getId();
    }
}