/** * 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.frontend.action.systems; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; import org.apache.log4j.Logger; import com.redhat.rhn.common.conf.ConfigDefaults; import com.redhat.rhn.common.db.datasource.DataResult; import com.redhat.rhn.common.hibernate.LookupException; import com.redhat.rhn.common.validator.ValidatorException; import com.redhat.rhn.domain.rhnpackage.Package; import com.redhat.rhn.domain.rhnpackage.PackageFactory; import com.redhat.rhn.domain.rhnset.RhnSet; import com.redhat.rhn.domain.session.WebSession; import com.redhat.rhn.domain.user.User; import com.redhat.rhn.frontend.dto.SystemOverview; import com.redhat.rhn.frontend.dto.SystemSearchResult; import com.redhat.rhn.frontend.struts.RequestContext; import com.redhat.rhn.manager.rhnset.RhnSetDecl; import com.redhat.rhn.manager.session.SessionManager; import com.redhat.rhn.manager.system.SystemManager; import com.redhat.rhn.manager.user.UserManager; import redstone.xmlrpc.XmlRpcClient; import redstone.xmlrpc.XmlRpcFault; /** * SystemSearchHelper * This will make calls to the XMLRPC Search Server * @version $Rev: 1 $ */ public class SystemSearchHelper { private static Logger log = Logger.getLogger(SystemSearchHelper.class); public static final String NAME_AND_DESCRIPTION = "systemsearch_name_and_description"; public static final String ID = "systemsearch_id"; public static final String CUSTOM_INFO = "systemsearch_custom_info"; public static final String SNAPSHOT_TAG = "systemsearch_snapshot_tag"; public static final String CHECKIN = "systemsearch_checkin"; public static final String REGISTERED = "systemsearch_registered"; public static final String CPU_MODEL = "systemsearch_cpu_model"; public static final String CPU_MHZ_LT = "systemsearch_cpu_mhz_lt"; public static final String CPU_MHZ_GT = "systemsearch_cpu_mhz_gt"; public static final String NUM_CPUS_LT = "systemsearch_num_of_cpus_lt"; public static final String NUM_CPUS_GT = "systemsearch_num_of_cpus_gt"; public static final String RAM_LT = "systemsearch_ram_lt"; public static final String RAM_GT = "systemsearch_ram_gt"; public static final String HW_DESCRIPTION = "systemsearch_hwdevice_description"; public static final String HW_DRIVER = "systemsearch_hwdevice_driver"; public static final String HW_DEVICE_ID = "systemsearch_hwdevice_device_id"; public static final String HW_VENDOR_ID = "systemsearch_hwdevice_vendor_id"; public static final String DMI_SYSTEM = "systemsearch_dmi_system"; public static final String DMI_BIOS = "systemsearch_dmi_bios"; public static final String DMI_ASSET = "systemsearch_dmi_asset"; public static final String HOSTNAME = "systemsearch_hostname"; public static final String IP = "systemsearch_ip"; public static final String IP6 = "systemsearch_ipv6"; public static final String INSTALLED_PACKAGES = "systemsearch_installed_packages"; public static final String NEEDED_PACKAGES = "systemsearch_needed_packages"; public static final String RUNNING_KERNEL = "systemsearch_running_kernel"; public static final String LOC_ADDRESS = "systemsearch_location_address"; public static final String LOC_BUILDING = "systemsearch_location_building"; public static final String LOC_ROOM = "systemsearch_location_room"; public static final String LOC_RACK = "systemsearch_location_rack"; public static final String UUID = "systemsearch_uuid"; /** * These vars store the name of a lucene index on the search server */ public static final String PACKAGES_INDEX = "package"; public static final String SERVER_INDEX = "server"; public static final String HARDWARE_DEVICE_INDEX = "hwdevice"; public static final String SNAPSHOT_TAG_INDEX = "snapshotTag"; public static final String SERVER_CUSTOM_INFO_INDEX = "serverCustomInfo"; public static final Double PACKAGE_SCORE_THRESHOLD = 0.5; protected SystemSearchHelper() { } public static final String[] OPT_GROUPS_TITLES = {"systemsearch.jsp.details", "systemsearch.jsp.activity", "systemsearch.jsp.hardware", "systemsearch.jsp.devices", "systemsearch.jsp.dmiinfo", "systemsearch.jsp.networkinfo", "systemsearch.jsp.packages", "systemsearch.jsp.location"}; public static final String[][] OPT_GROUPS = { /* details */ { SystemSearchHelper.NAME_AND_DESCRIPTION, SystemSearchHelper.ID, SystemSearchHelper.CUSTOM_INFO, SystemSearchHelper.SNAPSHOT_TAG, SystemSearchHelper.RUNNING_KERNEL, SystemSearchHelper.UUID }, /* activity group */ { SystemSearchHelper.CHECKIN, SystemSearchHelper.REGISTERED }, /* hardware group */ { SystemSearchHelper.CPU_MODEL, SystemSearchHelper.CPU_MHZ_LT, SystemSearchHelper.CPU_MHZ_GT, SystemSearchHelper.NUM_CPUS_LT, SystemSearchHelper.NUM_CPUS_GT, SystemSearchHelper.RAM_LT, SystemSearchHelper.RAM_GT }, /* device group */ { SystemSearchHelper.HW_DESCRIPTION, SystemSearchHelper.HW_DRIVER, SystemSearchHelper.HW_DEVICE_ID, SystemSearchHelper.HW_VENDOR_ID }, /* dmiinfo */ { SystemSearchHelper.DMI_SYSTEM, SystemSearchHelper.DMI_BIOS, SystemSearchHelper.DMI_ASSET }, /* network info */ { SystemSearchHelper.HOSTNAME, SystemSearchHelper.IP, SystemSearchHelper.IP6 }, /* packages */ { SystemSearchHelper.INSTALLED_PACKAGES, SystemSearchHelper.NEEDED_PACKAGES }, /* location */ { SystemSearchHelper.LOC_ADDRESS, SystemSearchHelper.LOC_BUILDING, SystemSearchHelper.LOC_ROOM, SystemSearchHelper.LOC_RACK } }; /** * Returns a DataResult of SystemSearchResults which are based on the user's search * criteria * @param ctx request context * @param searchString string to search on * @param viewMode what field to search * @param invertResults whether the results should be inverted * @param whereToSearch whether to search through all user visible systems or the * systems selected in the SSM * @param isFineGrained fine grained search * @return DataResult of SystemSearchResults based on user's search criteria * @throws XmlRpcFault on xmlrpc error * @throws MalformedURLException on bad search server address */ public static DataResult systemSearch(RequestContext ctx, String searchString, String viewMode, Boolean invertResults, String whereToSearch, Boolean isFineGrained) throws XmlRpcFault, MalformedURLException { WebSession session = ctx.getWebSession(); String key = session.getKey(); return systemSearch(key, searchString, viewMode, invertResults, whereToSearch, isFineGrained); } /** * Returns a DataResult of SystemSearchResults which are based on the user's search * criteria * @param sessionKey key for this session * @param searchString string to search on * @param viewMode what field to search * @param invertResults whether the results should be inverted * @param whereToSearch whether to search through all user visible systems or the * systems selected in the SSM * @param isFineGrained fine grained search * @return DataResult of SystemSearchResults based on user's search criteria * @throws XmlRpcFault on xmlrpc error * @throws MalformedURLException on bad search server address */ public static DataResult systemSearch(String sessionKey, String searchString, String viewMode, Boolean invertResults, String whereToSearch, Boolean isFineGrained) throws XmlRpcFault, MalformedURLException { WebSession session = SessionManager.loadSession(sessionKey); Long sessionId = session.getId(); User user = session.getUser(); //Make sure there was a valid user in the session. If not, the session is invalid. if (user == null) { throw new LookupException("Could not find a valid user for session with key: " + sessionKey); } /** * Determine what index to search and form the query */ Map<String, String> params = preprocessSearchString(searchString, viewMode); String query = params.get("query"); String index = params.get("index"); /** * Contact the XMLRPC search server and get back the results */ List results = performSearch(sessionId, index, query, isFineGrained); /** * We need to translate these results into a fleshed out DTO object which * can be displayed.by the JSP */ Map serverIds = null; if (PACKAGES_INDEX.equals(index)) { serverIds = getResultMapFromPackagesIndex(user, results, viewMode); } else if (SERVER_INDEX.equals(index)) { serverIds = getResultMapFromServerIndex(results); } else if (HARDWARE_DEVICE_INDEX.equals(index)) { serverIds = getResultMapFromHardwareDeviceIndex(results); } else if (SNAPSHOT_TAG_INDEX.equals(index)) { serverIds = getResultMapFromSnapshotTagIndex(results); } else if (SERVER_CUSTOM_INFO_INDEX.equals(index)) { serverIds = getResultMapFromServerCustomInfoIndex(results); } else { log.warn("Unknown index: " + index); log.warn("Defaulting to treating this as a " + SERVER_INDEX + " index"); serverIds = getResultMapFromServerIndex(results); } if (invertResults) { serverIds = invertResults(user, serverIds); } // Assuming we search all systems by default, unless whereToSearch states // to use the System Set Manager systems only. In that case we simply do a // filter of returned search results to only return IDs which are in SSM if ("system_list".equals(whereToSearch)) { serverIds = filterOutIdsNotInSSM(user, serverIds); } DataResult retval = processResultMap(user, serverIds, viewMode); return retval; } protected static List performSearch(Long sessionId, String index, String query, Boolean isFineGrained) throws XmlRpcFault, MalformedURLException { log.info("Performing system search: index = " + index + ", query = " + query); XmlRpcClient client = new XmlRpcClient( ConfigDefaults.get().getSearchServerUrl(), true); List args = new ArrayList(); args.add(sessionId); args.add(index); args.add(query); args.add(isFineGrained); List results = (List)client.invoke("index.search", args); if (log.isDebugEnabled()) { log.debug("results = [" + results + "]"); } if (results.isEmpty()) { return Collections.EMPTY_LIST; } return results; } protected static Map<String, String> preprocessSearchString(String searchstring, String mode) { StringBuilder buf = new StringBuilder(searchstring.length()); String[] tokens = searchstring.split(" "); for (String s : tokens) { if (s.trim().equalsIgnoreCase("AND") || s.trim().equalsIgnoreCase("OR") || s.trim().equalsIgnoreCase("NOT")) { s = s.toUpperCase(); } else { // Escape colons in IPv6 address automatically. // (colon is a special lucene character) if (IP6.equals(mode)) { s = s.replace(":", "\\:"); } } buf.append(s); buf.append(" "); } String terms = buf.toString().trim(); String query, index; if (NAME_AND_DESCRIPTION.equals(mode)) { query = "name:(" + terms + ") description:(" + terms + ")"; index = SERVER_INDEX; } else if (ID.equals(mode)) { // 'system_id' is the tokenized form of ID in the search server query = "system_id:(" + terms + ")"; index = SERVER_INDEX; } else if (CUSTOM_INFO.equals(mode)) { query = "value:(" + terms + ")"; index = SERVER_CUSTOM_INFO_INDEX; } else if (SNAPSHOT_TAG.equals(mode)) { query = "name:(" + terms + ")"; index = SNAPSHOT_TAG_INDEX; } else if (CHECKIN.equals(mode)) { Integer numDays = Integer.parseInt(terms); Calendar startDate = Calendar.getInstance(); // SearchRange: [EPOCH - TargetDate] startDate.add(Calendar.DATE, -1 * numDays); query = "checkin:[\"" + formatDateString(new Date(0)) + "\" TO \"" + formatDateString(startDate.getTime()) + "\"]"; index = SERVER_INDEX; } else if (REGISTERED.equals(mode)) { Integer numDays = Integer.parseInt(terms); Calendar startDate = Calendar.getInstance(); // SearchRange: [TargetDate - NOW] startDate.add(Calendar.DATE, (-1 * numDays) - 1); query = "registered:{\"" + formatDateString(startDate.getTime()) + "\" TO \"" + formatDateString( Calendar.getInstance().getTime()) + "\"}"; index = SERVER_INDEX; } else if (CPU_MODEL.equals(mode)) { query = "cpuModel:(" + terms + ")"; index = SERVER_INDEX; } else if (CPU_MHZ_LT.equals(mode)) { query = "cpuMHz:{0 TO " + terms + "}"; index = SERVER_INDEX; } else if (CPU_MHZ_GT.equals(mode)) { query = "cpuMHz:{" + terms + " TO " + Long.MAX_VALUE + "}"; index = SERVER_INDEX; } else if (NUM_CPUS_LT.equals(mode)) { query = "cpuNumberOfCpus:{0 TO " + terms + "}"; index = SERVER_INDEX; } else if (NUM_CPUS_GT.equals(mode)) { query = "cpuNumberOfCpus:{" + terms + " TO " + Long.MAX_VALUE + "}"; index = SERVER_INDEX; } else if (RAM_LT.equals(mode)) { query = "ram:{0 TO " + terms + "}"; index = SERVER_INDEX; } else if (RAM_GT.equals(mode)) { query = "ram:{" + terms + " TO " + Long.MAX_VALUE + "}"; index = SERVER_INDEX; } else if (HW_DESCRIPTION.equals(mode)) { query = "description:(" + terms + ")"; index = HARDWARE_DEVICE_INDEX; } else if (HW_DRIVER.equals(mode)) { query = "driver:(" + terms + ")"; index = HARDWARE_DEVICE_INDEX; } else if (HW_DEVICE_ID.equals(mode)) { query = "deviceId:(" + terms + ")"; index = HARDWARE_DEVICE_INDEX; } else if (HW_VENDOR_ID.equals(mode)) { query = "vendorId:(" + terms + ")"; index = HARDWARE_DEVICE_INDEX; } else if (DMI_SYSTEM.equals(mode)) { query = "dmiSystem:(" + terms + ")"; index = SERVER_INDEX; } else if (DMI_BIOS.equals(mode)) { query = "dmiBiosVendor:(" + terms + ") dmiBiosVersion:(" + terms + ")" + " dmiBiosRelease:(" + terms + ")"; index = SERVER_INDEX; } else if (DMI_ASSET.equals(mode)) { query = "dmiAsset:(" + terms + ")"; index = SERVER_INDEX; } else if (HOSTNAME.equals(mode)) { query = "hostname:(" + terms + ")"; index = SERVER_INDEX; } else if (IP.equals(mode)) { query = "ipaddr:(" + terms + ")"; index = SERVER_INDEX; } else if (IP6.equals(mode)) { query = "ip6addr:(" + terms + ")"; index = SERVER_INDEX; } else if (INSTALLED_PACKAGES.equals(mode)) { query = "name:(" + terms + ")" + " filename:(" + terms + ")"; index = PACKAGES_INDEX; } else if (NEEDED_PACKAGES.equals(mode)) { query = "name:(" + terms + ")" + " filename:(" + terms + ")"; index = PACKAGES_INDEX; } else if (RUNNING_KERNEL.equals(mode)) { query = "runningKernel:(" + terms + ")"; index = SERVER_INDEX; } else if (LOC_ADDRESS.equals(mode)) { query = "address1:(" + terms + ") address2:(" + terms + ")"; index = SERVER_INDEX; } else if (LOC_BUILDING.equals(mode)) { query = "building:(" + terms + ")"; index = SERVER_INDEX; } else if (LOC_ROOM.equals(mode)) { query = "room:(" + terms + ")"; index = SERVER_INDEX; } else if (LOC_RACK.equals(mode)) { query = "rack:(" + terms + ")"; index = SERVER_INDEX; } else if (UUID.equals(mode)) { query = "uuid:(" + terms + ")"; index = SERVER_INDEX; } else { throw new ValidatorException("Mode: " + mode + " not supported."); } Map<String, String> retval = new HashMap<String, String>(); retval.put("query", query); retval.put("index", index); return retval; } /** * We did a normal package search and got back a List of results for * the package name(s), now we correlate that to what systems have those * installed, or need them to be updated. * * TODO: Look into a quicker/more efficient implementation. This appears to * work....but I think it can be become quicker. */ protected static Map getResultMapFromPackagesIndex(User user, List searchResults, String viewMode) { // this is our main result Map which we will return, it's keys // represent the list of server Ids this search yielded Map serverMaps = new HashMap(); log.info("Entering getResultMapFromPackagesIndex() searchResults.size() = " + searchResults.size()); for (int index = 0; index < searchResults.size(); index++) { Map result = (Map)searchResults.get(index); Map pkgItem = new HashMap(); pkgItem.put("rank", result.get("rank")); pkgItem.put("score", result.get("score")); pkgItem.put("name", result.get("name")); pkgItem.put("pkgId", result.get("id")); /** * Ensure we process at least the first result. * Remeber, first result might be a group of packages with the same * name but different archs, we want to process the whole group. * Therefore for cases of low score quality, we'll set min_score * to the score of the first hit, this will allow the first group * of similarly scored results to be processed, all further results will * be dropped. * For case of a high scoring result, PACKAGE_SCORE_THRESHOLD still * limits the results returned. */ Double currentScore = (Double)result.get("score"); Double minScore = PACKAGE_SCORE_THRESHOLD; if (index == 0) { if (currentScore <= PACKAGE_SCORE_THRESHOLD) { minScore = currentScore; } } log.info("Iteration " + index + ", Name = " + result.get("name") + ", Score = " + currentScore); if (currentScore < minScore) { log.info("SystemSearchHelper.getResultMapFromPackagesIndex() " + " skipping result<" + result.get("name") + "> score = " + result.get("score") + " it is below threshold: " + minScore); continue; } Long pkgId = Long.valueOf((String)result.get("id")); List<Long> serverIds = null; if (INSTALLED_PACKAGES.equals(viewMode)) { serverIds = getSystemsByInstalledPackageId(user, pkgId); } if (NEEDED_PACKAGES.equals(viewMode)) { serverIds = getSystemsByNeededPackageId(user, pkgId); } if (serverIds.size() < 1) { continue; } Package pkg = PackageFactory.lookupByIdAndUser(pkgId, user); if (pkg == null) { log.warn("SystemSearchHelper.getResultMapFromPackagesIndex() " + " problem when looking up package id <" + pkgId + " PackageFactory.lookupByIdAndUser returned null."); continue; } log.info("Package " + pkg.getNameEvra() + ", id = " + pkgId + ", score = " + currentScore + ", serverIds associated with package = " + serverIds.size()); for (Long s : serverIds) { if (serverMaps.containsKey(s)) { Map m = (Map)serverMaps.get(s); Double score = (Double)result.get("score"); if (score > (Double)m.get("score")) { m.put("score", score); m.put("packageName", pkg.getNameEvra()); } } else { // Create the serverInfo which we will be returning back Map serverInfo = new HashMap(); serverInfo.put("score", result.get("score")); serverInfo.put("matchingField", "packageName"); serverInfo.put("matchingFieldValue", pkg.getNvrea()); serverInfo.put("packageName", pkg.getNameEvra()); serverMaps.put(s, serverInfo); if (log.isDebugEnabled()) { log.debug("created new map for server id: " + s + ", searched with packageName: " + pkg.getNameEvra() + " score = " + serverInfo.get("score")); } } } // end for looping over servers per packageId } // end looping over packageId return serverMaps; } protected static Map getResultMapFromServerIndex(List searchResults) { if (log.isDebugEnabled()) { log.debug("forming results for: " + searchResults); } Map serverIds = new HashMap(); for (Object obj : searchResults) { Map result = (Map)obj; Map serverItem = new HashMap(); serverItem.put("rank", result.get("rank")); serverItem.put("score", result.get("score")); serverItem.put("name", result.get("name")); String matchingField = (String)result.get("matchingField"); if (matchingField.length() == 0) { matchingField = (String)result.get("name"); } else if ("system_id".compareTo(matchingField) == 0) { //system_id was used to allow tokenized searches on id //we want to treat it as if 'id' was used for all lookups matchingField = "id"; } serverItem.put("matchingField", matchingField); serverItem.put("matchingFieldValue", result.get("matchingFieldValue")); if (log.isDebugEnabled()) { log.debug("creating new map for system id: " + result.get("id") + " new map = " + serverItem); } serverIds.put(Long.valueOf((String)result.get("id")), serverItem); } return serverIds; } protected static Map getResultMapFromHardwareDeviceIndex(List searchResults) { if (log.isDebugEnabled()) { log.debug("forming results for: " + searchResults); } Map serverIds = new HashMap(); for (Object obj : searchResults) { Map result = (Map)obj; Long sysId = Long.valueOf((String)result.get("serverId")); if (serverIds.containsKey(sysId)) { Map priorResult = (Map)serverIds.get(sysId); Double priorScore = (Double)priorResult.get("score"); Double thisScore = (Double)result.get("score"); if (priorScore >= thisScore) { // We only want to capture the best match of a hwdevice for each system continue; } } Map serverItem = new HashMap(); serverItem.put("rank", result.get("rank")); serverItem.put("score", result.get("score")); serverItem.put("name", result.get("name")); serverItem.put("hwdeviceId", result.get("id")); String matchingField = (String)result.get("matchingField"); if (matchingField.length() == 0) { matchingField = (String)result.get("name"); } serverItem.put("matchingField", matchingField); serverItem.put("matchingFieldValue", result.get("matchingFieldValue")); if (log.isDebugEnabled()) { log.debug("creating new map for serverId = " + result.get("serverId") + ", hwdevice id: " + result.get("id") + " new map = " + serverItem); } serverIds.put(sysId, serverItem); } return serverIds; } protected static Map getResultMapFromSnapshotTagIndex(List searchResults) { if (log.isDebugEnabled()) { log.debug("forming results for: " + searchResults); } Map serverIds = new HashMap(); for (Object obj : searchResults) { Map result = (Map)obj; Map serverItem = new HashMap(); serverItem.put("rank", result.get("rank")); serverItem.put("score", result.get("score")); serverItem.put("name", result.get("name")); serverItem.put("snapshotId", result.get("snapshotId")); String matchingField = (String)result.get("matchingField"); if (matchingField.length() == 0) { matchingField = (String)result.get("name"); } serverItem.put("matchingField", matchingField); serverItem.put("matchingFieldValue", result.get("matchingFieldValue")); if (log.isDebugEnabled()) { log.debug("creating new map for serverId = " + result.get("serverId") + ", snapshotID: " + result.get("snapshotId") + " new map = " + serverItem); } serverIds.put(Long.valueOf((String)result.get("serverId")), serverItem); } return serverIds; } protected static Map getResultMapFromServerCustomInfoIndex(List searchResults) { if (log.isDebugEnabled()) { log.debug("forming results for: " + searchResults); } Map serverIds = new HashMap(); for (Object obj : searchResults) { Map result = (Map)obj; Map serverItem = new HashMap(); serverItem.put("rank", result.get("rank")); serverItem.put("score", result.get("score")); serverItem.put("name", result.get("value")); serverItem.put("snapshotId", result.get("snapshotId")); String matchingField = (String)result.get("matchingField"); if (matchingField.length() == 0) { matchingField = (String)result.get("value"); } serverItem.put("matchingField", matchingField); String matchingFieldValue = (String)result.get("matchingFieldValue"); if (matchingFieldValue.length() == 0) { matchingFieldValue = (String)result.get("value"); } serverItem.put("matchingFieldValue", matchingFieldValue); if (log.isDebugEnabled()) { log.debug("creating new map for serverId = " + result.get("serverId") + ", customValueID: " + result.get("id") + " new map = " + serverItem); } serverIds.put(Long.valueOf((String)result.get("serverId")), serverItem); } return serverIds; } protected static DataResult processResultMap(User userIn, Map serverIds, String viewMode) { DataResult<SystemSearchResult> serverList = UserManager.visibleSystemsAsDtoFromList(userIn, new ArrayList(serverIds.keySet())); if (serverList == null) { return null; } for (SystemSearchResult sr : serverList) { Map details = (Map)serverIds.get(sr.getId()); String field = (String)details.get("matchingField"); sr.setMatchingField(field); if (details.containsKey("packageName")) { sr.setPackageName((String)details.get("packageName")); } if (details.containsKey("hwdeviceId")) { Long hwId = Long.parseLong((String)details.get("hwdeviceId")); sr.setHw(SystemManager.getHardwareDeviceById(hwId)); // we want the matching field to call into the HardwareDeviceDto // to return back the value of what matched sr.setMatchingField("hw." + field); } if (details.containsKey("uuid")) { sr.setUuid((String)details.get("uuid")); } if (details.containsKey("rank")) { sr.setRank((Integer)details.get("rank")); } if (details.containsKey("score")) { sr.setScore((Double)details.get("score")); } if (details.containsKey("matchingFieldValue")) { sr.setMatchingFieldValue((String)details.get("matchingFieldValue")); } } if (log.isDebugEnabled()) { log.debug("sorting server data based on score from lucene search"); } /** RangeQueries return a constant score of 1.0 for anything that matches. * Therefore we need to do more work to understand how to best sort results. * Sorting will be done based on value for 'matchingFieldValue', this is a best * guess from the search server of what field in the document most influenced * the result. * */ if (REGISTERED.equals(viewMode) || CPU_MHZ_GT.equals(viewMode) || NUM_CPUS_GT.equals(viewMode) || RAM_GT.equals(viewMode)) { // We want to sort Low to High SearchResultMatchedFieldComparator comparator = new SearchResultMatchedFieldComparator(serverIds); Collections.sort(serverList, comparator); } else if (CHECKIN.equals(viewMode) || CPU_MHZ_LT.equals(viewMode) || NUM_CPUS_LT.equals(viewMode) || RAM_LT.equals(viewMode)) { // We want to sort High to Low SearchResultMatchedFieldComparator comparator = new SearchResultMatchedFieldComparator(serverIds, false); Collections.sort(serverList, comparator); } else { SearchResultScoreComparator scoreComparator = new SearchResultScoreComparator(serverIds); Collections.sort(serverList, scoreComparator); } if (log.isDebugEnabled()) { log.debug("sorted server data = " + serverList); } return serverList; } protected static List<Long> getSystemsByInstalledPackageId(User user, Long pkgId) { List serverIds = new ArrayList<Long>(); List<SystemOverview> data = SystemManager.listSystemsWithPackage(user, pkgId); if (data == null) { log.info("SystemSearchHelper.getSystemsByInstalledPackageId(" + pkgId + ") got back null."); return null; } for (SystemOverview so : data) { serverIds.add(so.getId()); } return serverIds; } protected static List<Long> getSystemsByNeededPackageId(User user, Long pkgId) { List serverIds = new ArrayList<Long>(); List<SystemOverview> data = SystemManager.listSystemsWithNeededPackage(user, pkgId); if (data == null) { log.info("SystemSearchHelper.getSystemsByNeededPackageId(" + pkgId + ") got back null."); return null; } for (SystemOverview so : data) { serverIds.add(so.getId()); } return serverIds; } protected static Map filterOutIdsNotInSSM(User user, Map ids) { RhnSet systems = RhnSetDecl.SYSTEMS.get(user); Object[] keys = ids.keySet().toArray(); for (Object key : keys) { if (!systems.contains((Long)key)) { log.debug("SystemSearchHelper.filterOutIdsNotInSSM() removing system id " + key + ", because it is not in the SystemSetManager list of ids"); ids.remove(key); } } return ids; } protected static Map invertResults(User user, Map ids) { // Hack to guess at what the matchingField should be, use the "matchingField" from // the first item in the passed in Map of ids String matchingField = ""; if (!ids.isEmpty()) { Object key = ids.keySet().toArray()[0]; Map firstItem = (Map)ids.get(key); matchingField = (String)firstItem.get("matchingField"); } log.info("Will use <" + matchingField + "> as the value to supply for " + "matchingField in all of these invertMatches"); // Get list of all SystemIds and save to new Map Map invertedIds = new HashMap(); DataResult<SystemOverview> dr = SystemManager.systemList(user, null); log.info(dr.size() + " systems came back as the total number of visible systems " + "to this user"); for (SystemOverview so : dr) { log.debug("Adding system id: " + so.getId() + " to allIds map"); Map info = new HashMap(); info.put("matchingField", matchingField); invertedIds.put(so.getId(), info); } // Remove each entry which matches passed in ids Object[] currentIds = ids.keySet().toArray(); for (Object id : currentIds) { if (invertedIds.containsKey(id)) { invertedIds.remove(id); log.debug("removed " + id + " from allIds"); } } log.info("returning " + invertedIds.size() + " system ids as the inverted results"); return invertedIds; } protected static String formatDateString(Date d) { String dateFormat = "yyyyMMddHHmm"; java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(dateFormat); // Lucene uses GMT for indexing sdf.setTimeZone(TimeZone.getTimeZone("GMT")); return sdf.format(d); } /** * Will compare two SystemOverview objects based on their score from a * lucene search Creates a list ordered from highest score to lowest */ public static class SearchResultScoreComparator implements Comparator { private int S1_FIRST = -1; private int S2_FIRST = 1; private int EQUAL = 0; protected Map results; protected SearchResultScoreComparator() { } /** * @param resultsIn * map of server related info to use for comparisons */ public SearchResultScoreComparator(Map resultsIn) { this.results = resultsIn; } /** * @param o1 systemOverview11 * @param o2 systemOverview2 * @return comparison info based on lucene score */ @Override public int compare(Object o1, Object o2) { SystemOverview sys1 = (SystemOverview) o1; SystemOverview sys2 = (SystemOverview) o2; Long serverId1 = sys1.getId(); Long serverId2 = sys2.getId(); if (results == null) { return compareByNameAndSID(sys1, sys2); } Map sMap1 = (Map) results.get(serverId1); Map sMap2 = (Map) results.get(serverId2); if ((sMap1 == null) && (sMap2 == null)) { return compareByNameAndSID(sys1, sys2); } if (sMap1 == null) { return S2_FIRST; } if (sMap2 == null) { return S1_FIRST; } Double score1 = (sMap1.containsKey("score") ? (Double) sMap1.get("score") : null); Double score2 = (sMap2.containsKey("score") ? (Double) sMap2.get("score") : null); if ((score1 == null) && (score2 == null)) { return compareByNameAndSID(sys1, sys2); } if (score1 == null) { return S2_FIRST; } if (score2 == null) { return S1_FIRST; } /* * Note: We want a list which goes from highest score to lowest * score, so we are reversing the order of comparison. */ score1 *= -1.0d; score2 *= -1.0d; /* * 2/19/09 Adding to this for bz# 483177 Customer request that we * also order by systemid, they request that when the same hostname * has been registered many times and shows up in search, we sort by * sysid with the highest systemid at the top. */ if (Math.abs(score1 - score2) < .001) { // Lucene might give slight score differences to entries which are // practically identical except for maybe registration time, etc. // therefore putting a fudgefactor so we can treat systems in this // range as having the same score. return compareByNameAndSID(sys1, sys2); } return score1.compareTo(score2); } // Sort by profile-name if there is one, then by reverse-sid-order private int compareByNameAndSID(SystemOverview sys1, SystemOverview sys2) { if ((sys1.getName() == null) && (sys2.getName() == null)) { return sys2.getId().compareTo(sys1.getId()); } if (sys1.getName() == null) { return S2_FIRST; } if (sys2.getName() == null) { return S1_FIRST; } if (sys1.getName().equals(sys2.getName())) { // We want highest id to be on top return sys2.getId().compareTo(sys1.getId()); } else { return sys1.getName().compareTo(sys2.getName()); } } } /** * * Compares search results by 'matchingFieldValue' * */ public static class SearchResultMatchedFieldComparator implements Comparator { protected Map results; protected boolean sortLowToHigh; protected SearchResultMatchedFieldComparator() { } /** * @param resultsIn map of server related info to use for comparisons */ public SearchResultMatchedFieldComparator(Map resultsIn) { this.results = resultsIn; this.sortLowToHigh = true; } /** * @param resultsIn map of server related info to use for comparisons * @param sortLowToHighIn sort order boolean */ public SearchResultMatchedFieldComparator(Map resultsIn, boolean sortLowToHighIn) { this.results = resultsIn; this.sortLowToHigh = sortLowToHighIn; } /** * @param o1 systemOverview11 * @param o2 systemOverview2 * @return comparison info based on matchingFieldValue */ @Override public int compare(Object o1, Object o2) { SystemOverview sys1 = (SystemOverview)o1; SystemOverview sys2 = (SystemOverview)o2; Long serverId1 = sys1.getId(); Long serverId2 = sys2.getId(); if (results == null) { return 0; } Map sMap1 = (Map)results.get(serverId1); Map sMap2 = (Map)results.get(serverId2); if ((sMap1 == null) && (sMap2 == null)) { return 0; } if ((sMap1 == null) && (sMap2 != null)) { return -1; } if ((sMap1 != null) && (sMap2 == null)) { return 1; } String val1 = (String)sMap1.get("matchingFieldValue"); String val2 = (String)sMap2.get("matchingFieldValue"); if ((val1 == null) && (val2 == null)) { return 0; } if ((val1 == null) && (val2 != null)) { return -1; } if ((val1 != null) && (val2 == null)) { return 1; } try { Long lng1 = Long.parseLong(val1); Long lng2 = Long.parseLong(val2); if (sortLowToHigh) { return lng1.compareTo(lng2); } return lng2.compareTo(lng1); } catch (NumberFormatException e) { // String isn't a Long so continue; } try { Double doub1 = Double.parseDouble(val1); Double doub2 = Double.parseDouble(val2); if (sortLowToHigh) { return doub1.compareTo(doub2); } return doub2.compareTo(doub1); } catch (NumberFormatException e) { // String isn't a Double so continue; } // Fallback to standard string sort if (sortLowToHigh) { return val1.compareTo(val2); } return val2.compareTo(val1); } } }