/**
* Copyright (c) 2009--2016 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.domain.rhnpackage;
import com.redhat.rhn.common.db.datasource.CachedStatement;
import com.redhat.rhn.common.db.datasource.DataResult;
import com.redhat.rhn.common.db.datasource.ModeFactory;
import com.redhat.rhn.common.db.datasource.QuerySanitizer;
import com.redhat.rhn.common.db.datasource.SelectMode;
import com.redhat.rhn.common.hibernate.HibernateFactory;
import com.redhat.rhn.domain.org.Org;
import com.redhat.rhn.domain.server.InstalledPackage;
import com.redhat.rhn.domain.server.Server;
import com.redhat.rhn.domain.user.User;
import com.redhat.rhn.frontend.action.channel.PackageSearchAction;
import com.redhat.rhn.frontend.dto.BooleanWrapper;
import com.redhat.rhn.frontend.dto.PackageOverview;
import com.redhat.rhn.manager.user.UserManager;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* PackageFactory
* @version $Rev$
*/
public class PackageFactory extends HibernateFactory {
private static PackageFactory singleton = new PackageFactory();
private static Logger log = Logger.getLogger(PackageFactory.class);
public static final PackageKeyType PACKAGE_KEY_TYPE_GPG = lookupKeyTypeByLabel("gpg");
public static final String ARCH_TYPE_RPM = "rpm";
public static final String ARCH_TYPE_TAR = "tar";
private PackageFactory() {
super();
}
/**
* Get the Logger for the derived class so log messages show up on the
* correct class
*/
protected Logger getLogger() {
return log;
}
/**
* Lookup a Package by its ID
* @param id to search for
* @return the Package found
*/
private static Package lookupById(Long id) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("id", id);
return (Package) singleton.lookupObjectByNamedQuery("Package.findById", params);
}
/**
* Returns true if the Package with the given name and evr ids exists in the
* Channel whose id is cid.
* @param cid Channel id to look in
* @param nameId Package name id
* @param evrId Package evr id
* @return true if the Package with the given name and evr ids exists in the
* Channel whose id is cid.
*/
public static boolean isPackageInChannel(Long cid, Long nameId, Long evrId) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("cid", cid);
params.put("name_id", nameId);
params.put("evr_id", evrId);
SelectMode m = ModeFactory.getMode("Channel_queries", "is_package_in_channel");
DataResult dr = m.execute(params);
if (dr.isEmpty()) {
return false;
}
BooleanWrapper bw = (BooleanWrapper) dr.get(0);
return bw.booleanValue();
}
/**
* Lookup a Package by the id, in the context of a given user. Does security
* check to verify that the user has access to the package.
* @param id of the Package to search for
* @param user the user doing the search
* @return the Package found
*/
public static Package lookupByIdAndUser(Long id, User user) {
return lookupByIdAndOrg(id, user.getOrg());
}
/**
* Lookup a Package by the id, in the context of a given org. Does security
* check to verify that the org has access to the package.
* @param id of the Package to search for
* @param org the org which much have access to the package
* @return the Package found
*/
public static Package lookupByIdAndOrg(Long id, Org org) {
if (!UserManager.verifyPackageAccess(org, id)) {
// User doesn't have access to the package... return null as if it
// doesn't exist.
return null;
}
Package pkg = lookupById(id);
return pkg;
}
/**
* Store the package provider.
* @param prov The object we are commiting.
*/
public static void save(PackageProvider prov) {
singleton.saveObject(prov);
}
/**
* Store the package delta.
* @param delta The object we are commiting.
*/
public static void save(PackageDelta delta) {
singleton.saveObject(delta);
}
/**
* Lookup a PackageArch by its id.
* @param id package arch label id sought.
* @return the PackageArch whose id matches the given id.
*/
public static PackageArch lookupPackageArchById(Long id) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("id", id);
return (PackageArch) singleton.lookupObjectByNamedQuery("PackageArch.findById",
params, true);
}
/**
* Lookup a PackageArch by its label.
* @param label package arch label sought.
* @return the PackageArch whose label matches the given label.
*/
public static PackageArch lookupPackageArchByLabel(String label) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("label", label);
return (PackageArch) singleton.lookupObjectByNamedQuery("PackageArch.findByLabel",
params, true);
}
/**
* List the Package objects by their Package Name
* @param pn to query by
* @return List of Package objects if found
*/
public static List listPackagesByPackageName(PackageName pn) {
Session session = HibernateFactory.getSession();
return session.getNamedQuery("Package.findByPackageName").setEntity("packageName",
pn).list();
}
/**
* lookup a PackageName object based on it's name, If one does not exist,
* create a new one and return it.
* @param pn the package name
* @return a PackageName object that has a matching name
*/
public static synchronized PackageName lookupOrCreatePackageByName(String pn) {
PackageName returned = lookupPackageName(pn);
if (returned == null) {
PackageName newName = new PackageName();
newName.setName(pn);
singleton.saveObject(newName);
return newName;
}
return returned;
}
/**
* lookup a PackageName object based on it's id, returns null if it does
* not exist
*
* @param id the package name id
* @return a PackageName object that has a matching id or null if that
* doesn't exist
*/
public static PackageName lookupPackageName(Long id) {
PackageName returned = (PackageName) HibernateFactory.getSession().getNamedQuery(
"PackageName.findById").setLong("id", id).uniqueResult();
return returned;
}
/**
* lookup a PackageName object based on it's name, returns null if it does
* not exist
*
* @param pn the package name
* @return a PackageName object that has a matching name or null if that
* doesn't exist
*/
private static PackageName lookupPackageName(String pn) {
PackageName returned = (PackageName) HibernateFactory.getSession().getNamedQuery(
"PackageName.findByName").setString("name", pn).uniqueResult();
return returned;
}
/**
* lookup orphaned packages, i.e. packages that are not contained in any
* channel
* @param org the org to check for
* @return a List of package objects that are not in any channel
*/
public static List lookupOrphanPackages(Org org) {
return HibernateFactory.getSession().getNamedQuery("Package.listOrphans")
.setEntity("org", org).list();
}
/**
* Find a package based off of the NEVRA
* @param org the org that owns the package
* @param name the name to search for
* @param version the version to search for
* @param release the release to search for
* @param epoch if epoch is null, the best match for epoch will be used.
* @param arch the arch to search for
* @return the requested Package
*/
public static List<Package> lookupByNevra(Org org, String name, String version,
String release, String epoch, PackageArch arch) {
List<Package> packages = HibernateFactory.getSession().getNamedQuery(
"Package.lookupByNevra").setEntity("org", org).setString("name", name)
.setString("version", version).setString("release", release).setEntity(
"arch", arch).list();
if (epoch == null || packages.size() < 2) {
return packages;
}
for (Package pack : packages) {
if (!epoch.equals(pack.getPackageEvr().getEpoch())) {
packages.remove(pack);
}
}
return packages;
}
/**
* Returns an InstalledPackage object, given a server and package name to
* lookup the latest version of the package. Return null if the package
* doesn;t exist.
* @param name name of the package to lookup on
* @param server server where the give package was installed.
* @return the InstalledPackage with the given package name for the given
* server
*/
public static InstalledPackage lookupByNameAndServer(String name, Server server) {
PackageName packName = lookupPackageName(name);
Map<String, Object> params = new HashMap<String, Object>();
params.put("server", server);
params.put("name", packName);
List<InstalledPackage> original = singleton.listObjectsByNamedQuery(
"InstalledPackage.lookupByServerAndName", params);
if (original.isEmpty()) {
return null;
}
if (original.size() == 1) {
return original.get(0);
}
List<InstalledPackage> packs = new LinkedList<InstalledPackage>();
packs.addAll(original);
Collections.sort(packs);
return packs.get(packs.size() - 1);
}
/**
* Returns PackageOverviews from a search.
* @param pids List of package ids returned from search server.
* @param archLabels List of channel arch labels.
* @param relevantUserId user id to filter by if relevant or architecture search
* server the user can see is subscribed to
* @param filterChannelId channel id to filter by if channel search
* @param searchType type of search to do, one of "relevant", "channel",
* "architecture", or "all"
* @return PackageOverviews from a search.
*/
public static List<PackageOverview> packageSearch(List<Long> pids,
List<String> archLabels, Long relevantUserId, Long filterChannelId,
String searchType) {
Map<String, Object> params = new HashMap<String, Object>();
SelectMode m = null;
if (searchType.equals(PackageSearchAction.ARCHITECTURE)) {
if (!(archLabels != null && archLabels.size() > 0)) {
throw new MissingArchitectureException(
"archLabels must not be null for architecture search!");
}
// This makes me very sad. PreparedSatement.setObject does not allow
// you to pass in Lists or Arrays. We can't manually convert archLabels
// to a string and use the regular infrastructure because it will
// escape the quotes between architectures. The only thing we can do
// is to get the SelectMode and manually insert the architecture types
// before we continue. If we can get PreparedStatement to accept Lists
// then all this hackishness can go away. NOTE: we know that we have to
// guard against sql injection in this case. Notice that the archLabels
// will all be enclosed in single quotes. Valid archLabels will only
// contain alphanumeric, '-', and "_" characters. We will simply
// check and enforce that constraint, and then even if someone injected
// something we would either end up throwing an error or it would be
// in a string, and therefore not dangerous.
m = ModeFactory.getMode("Package_queries", "searchByIdAndArches");
CachedStatement cs = m.getQuery();
cs.modifyQuery(":channel_arch_labels", archLabels, new QuerySanitizer() {
@Override
public boolean isSanitary(String value) {
return value.matches("^[a-zA-Z0-9\\-_]*$");
}
});
}
else if (searchType.equals(PackageSearchAction.RELEVANT)) {
if (relevantUserId == null) {
throw new IllegalArgumentException(
"relevantUserId must not be null for relevant search!");
}
params.put("uid", relevantUserId);
m = ModeFactory.getMode("Package_queries", "relevantSearchById");
}
else if (searchType.equals(PackageSearchAction.CHANNEL)) {
if (filterChannelId == null) {
throw new IllegalArgumentException(
"filterChannelId must not be null for channel search!");
}
params.put("cid", filterChannelId);
m = ModeFactory.getMode("Package_queries", "searchByIdInChannel");
}
else {
m = ModeFactory.getMode("Package_queries", "searchById");
}
// SelectMode.execute will batch the size properly and CachedStatement.execute
// will create a comma separated string representation of the list of pids
DataResult result = m.execute(params, pids);
result.elaborate();
return result;
}
/**
* Lookup a package key type by label
* @param label the label of the type
* @return the key type
*/
public static PackageKeyType lookupKeyTypeByLabel(String label) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("label", label);
return (PackageKeyType) singleton.lookupObjectByNamedQuery(
"PackageKeyType.findByLabel", params);
}
/**
* Deletes a particular package object from hibernate. Note, currently This
* does not delete it from rhnServerNeededPackageCache so you probably want
* to use SystemManager.deletePackages() to do that instead. This does not
* also cleanup rhNPackageSource entries
* @param pack the package to delete
*/
public static void deletePackage(Package pack) {
HibernateFactory.getSession().delete(pack);
}
/**
* Deletes a particular package source object
* @param src the package source object
*/
public static void deletePackageSource(PackageSource src) {
HibernateFactory.getSession().delete(src);
}
/**
* Lookup package sources for a particular package
* @param pack the package associated with the package sources
* @return the list of package source objects
*/
public static List<PackageSource> lookupPackageSources(Package pack) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("pack", pack);
return singleton.listObjectsByNamedQuery("PackageSource.findByPackage", params);
}
/**
* Lookup package source by it's ID
* @param psid id of the source package
* @param org the org with access to the source package
* @return the package source
*/
public static PackageSource lookupPackageSourceByIdAndOrg(Long psid, Org org) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("id", psid);
params.put("org", org);
return (PackageSource) singleton.lookupObjectByNamedQuery(
"PackageSource.findByIdAndOrg", params);
}
/**
* Find other packages with the same NVRE but with different arches
* @param pack the package
* @return List of package objects
*/
public static List<Package> findPackagesWithDifferentArch(Package pack) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("evr", pack.getPackageEvr());
params.put("name", pack.getPackageName());
params.put("arch", pack.getPackageArch());
return singleton.listObjectsByNamedQuery("Package.findOtherArches", params);
}
/**
* Provides a mapping of arch type labels to sets of capabilities (ported from the if
* statement mess in package_type_capable of Package.pm). This should really
* be in the DB, but it's not :{ and it needs to be ported from perl.
*
* @return the map of arch label -> set of capabilities
*/
public static Map<String, Set<String>> getPackageCapabilityMap() {
Map<String, Set<String>> map = new HashMap<String, Set<String>>();
Set<String> rpmCaps = new HashSet<String>();
rpmCaps.add("dependencies");
rpmCaps.add("change_log");
rpmCaps.add("file_list");
rpmCaps.add("errata");
rpmCaps.add("remove");
rpmCaps.add("rpm");
map.put(PackageFactory.ARCH_TYPE_RPM, rpmCaps);
return map;
}
/**
* list package providers
* @return list of package providers
*/
public static List<PackageProvider> listPackageProviders() {
Map<String, Object> params = new HashMap<String, Object>();
List<PackageProvider> list = singleton.listObjectsByNamedQuery(
"PackageProvider.listProviders", params);
return list;
}
/**
* Looup a package provider by name
* @param name the name
* @return the package provider
*/
public static PackageProvider lookupPackageProvider(String name) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("name", name);
PackageProvider prov = (PackageProvider) singleton.lookupObjectByNamedQuery(
"PackageProvider.findByName", params);
return prov;
}
/**
* Deletes a package key
* @param key the key to delete
*/
public static void deletePackageKey(PackageKey key) {
HibernateFactory.getSession().delete(key);
}
/**
* Lookup a package key object
* @param key the key to lookup
* @return the package key
*/
public static PackageKey lookupPackageKey(String key) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("key", key);
PackageKey prov = (PackageKey) singleton.lookupObjectByNamedQuery(
"PackageKey.findByKey", params);
return prov;
}
/**
* List all package keys
* @return list of package key objects
*/
public static List<PackageKey> listPackageKeys() {
Map<String, Object> params = new HashMap<String, Object>();
List<PackageKey> prov = singleton.listObjectsByNamedQuery("PackageKey.listKeys",
params);
return prov;
}
/**
* Returns information, whether each package in the list is channel compatible
* and whether the org has accesds to
* @param orgId organization id
* @param channelId channel id
* @param packageIds list of package ids
* @return dataresult(id, package_arch_id, org_package, org_access, shared_access)
*/
public static DataResult getPackagesChannelArchCompatAndOrgAccess(
Long orgId, Long channelId, List<Long> packageIds) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("org_id", orgId);
params.put("channel_id", channelId);
SelectMode m = ModeFactory.getMode("Package_queries",
"channel_arch_and_org_access");
return m.execute(params, packageIds);
}
/**
* Returns package names that are shared between an erratum and a channel,
* with string representations of the versions in each.
* @param cid channel id
* @param eid errata id
* @param published whether the erratum is published or not
* @return list of maps, with keys of "name", "channel_version", and "errata_version"
*/
public static List<Map<String, String>> getErrataChannelIntersection(Long cid,
Long eid, boolean published) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("cid", cid);
params.put("eid", eid);
SelectMode m = null;
if (published) {
m = ModeFactory.getMode("Package_queries",
"channel_errata_intersection_published");
}
else {
m = ModeFactory.getMode("Package_queries",
"channel_errata_intersection_unpublished");
}
return m.execute(params);
}
}