001/* 002 * 003 * Copyright 2003-2007 Jive Software, 2025 Florian Schmaus. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.search; 018 019import java.util.List; 020import java.util.Map; 021import java.util.WeakHashMap; 022 023import org.jivesoftware.smack.Manager; 024import org.jivesoftware.smack.SmackException.NoResponseException; 025import org.jivesoftware.smack.SmackException.NotConnectedException; 026import org.jivesoftware.smack.XMPPConnection; 027import org.jivesoftware.smack.XMPPException.XMPPErrorException; 028import org.jivesoftware.smack.packet.IQ; 029 030import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 031import org.jivesoftware.smackx.xdata.form.FillableForm; 032import org.jivesoftware.smackx.xdata.form.Form; 033import org.jivesoftware.smackx.xdata.packet.DataForm; 034 035import org.jxmpp.jid.DomainBareJid; 036 037/** 038 * The UserSearchManager is a facade built upon Jabber Search Services (XEP-055) to allow for searching 039 * repositories on a Jabber Server. This implementation allows for transparency of implementation of 040 * searching (DataForms or No DataForms), but allows the user to simply use the DataForm model for both 041 * types of support. 042 * <pre> 043 * XMPPConnection connection = …; 044 * var searchService = UserSearchManager.getSearchServices(connection).get(0); 045 * var searchManager = UserSearchManager.getInstanceFor(connection); 046 * var sarchForm = searchManager.getSearchForm(searchService); 047 * var fillableForm = searchForm.getFillableForm(); 048 * 049 * // Check for the required fields in the form and fill them 050 * fillableForm.setAnswer("search", "John"); 051 * 052 * var results = searchOne.search(fillableForm, userSearchService); 053 * // Use results 054 * </pre> 055 * 056 * @author Derek DeMoro 057 */ 058public final class UserSearchManager extends Manager { 059 060 private static final Map<XMPPConnection, UserSearchManager> INSTANCES = new WeakHashMap<>(); 061 062 public static synchronized UserSearchManager getInstanceFor(XMPPConnection connection) { 063 var userSearchManager = INSTANCES.get(connection); 064 if (userSearchManager == null) { 065 userSearchManager = new UserSearchManager(connection); 066 INSTANCES.put(connection, userSearchManager); 067 } 068 return userSearchManager; 069 } 070 /** 071 * Creates a new UserSearchManager. 072 * 073 * @param connection the XMPPConnection to use. 074 */ 075 private UserSearchManager(XMPPConnection connection) { 076 super(connection); 077 } 078 079 /** 080 * Returns the form to fill out to perform a search. 081 * 082 * @param searchService the search service to query. 083 * @return the form to fill out to perform a search. 084 * @throws XMPPErrorException if there was an XMPP error returned. 085 * @throws NoResponseException if there was no response from the remote entity. 086 * @throws NotConnectedException if the XMPP connection is not connected. 087 * @throws InterruptedException if the calling thread was interrupted. 088 */ 089 public Form getSearchForm(DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 090 UserSearch search = new UserSearch(); 091 search.setType(IQ.Type.get); 092 search.setTo(searchService); 093 094 IQ response = connection().sendIqRequestAndWaitForResponse(search); 095 var dataForm = DataForm.from(response, UserSearch.NAMESPACE); 096 return new Form(dataForm); 097 } 098 099 /** 100 * Sends the filled out answer form to be sent and queried by the search service. 101 * 102 * @param filledForm the filled form with the query instructions. 103 * @param searchService the search service to use. (ex. search.jivesoftware.com) 104 * @return ReportedData the data found from the query. 105 * @throws XMPPErrorException if there was an XMPP error returned. 106 * @throws NoResponseException if there was no response from the remote entity. 107 * @throws NotConnectedException if the XMPP connection is not connected. 108 * @throws InterruptedException if the calling thread was interrupted. 109 */ 110 public ReportedData search(FillableForm filledForm, DomainBareJid searchService) 111 throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 112 UserSearch search = new UserSearch(); 113 search.setType(IQ.Type.set); 114 search.setTo(searchService); 115 search.addExtension(filledForm.getDataFormToSubmit()); 116 117 IQ response = connection().sendIqRequestAndWaitForResponse(search); 118 return ReportedData.getReportedDataFrom(response); 119 } 120 121 /** 122 * Sends the filled out answer form to be sent and queried by the search service. 123 * 124 * @param searchForm the <code>Form</code> to send for querying. 125 * @param searchService the search service to use. (ex. search.jivesoftware.com) 126 * @return ReportedData the data found from the query. 127 * @throws XMPPErrorException if there was an XMPP error returned. 128 * @throws NoResponseException if there was no response from the remote entity. 129 * @throws NotConnectedException if the XMPP connection is not connected. 130 * @throws InterruptedException if the calling thread was interrupted. 131 */ 132 public ReportedData sendSimpleSearchForm(DataForm searchForm, DomainBareJid searchService) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 133 SimpleUserSearch search = new SimpleUserSearch(); 134 search.setForm(searchForm); 135 search.setType(IQ.Type.set); 136 search.setTo(searchService); 137 138 SimpleUserSearch response = connection().sendIqRequestAndWaitForResponse(search); 139 return response.getReportedData(); 140 } 141 142 /** 143 * Returns a collection of search services found on the server. 144 * 145 * @param connection the connection to query for search services. 146 * @return a Collection of search services found on the server. 147 * @throws XMPPErrorException if there was an XMPP error returned. 148 * @throws NoResponseException if there was no response from the remote entity. 149 * @throws NotConnectedException if the XMPP connection is not connected. 150 * @throws InterruptedException if the calling thread was interrupted. 151 */ 152 public static List<DomainBareJid> getSearchServices(XMPPConnection connection) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 153 ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection); 154 return discoManager.findServices(UserSearch.NAMESPACE, false, true); 155 } 156}