1package org.jivesoftware.smackx;
2
3import java.util.ArrayList;
4import java.util.HashMap;
5import java.util.Iterator;
6import java.util.List;
7import java.util.Map;
8
9import org.jivesoftware.smack.Connection;
10import org.jivesoftware.smack.Roster;
11import org.jivesoftware.smack.RosterEntry;
12import org.jivesoftware.smack.XMPPException;
13import org.jivesoftware.smack.util.StringUtils;
14import org.jivesoftware.smackx.packet.DiscoverInfo;
15import org.jivesoftware.smackx.packet.DiscoverItems;
16import org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
17import org.jivesoftware.smackx.packet.DiscoverItems.Item;
18
19/**
20 * This class is the general entry point to gateway interaction (XEP-0100).
21 * This class discovers available gateways on the users servers, and
22 * can give you also a list of gateways the you user is registered with which
23 * are not on his server. All actual interaction with a gateway is handled in the
24 * class {@see Gateway}.
25 * @author Till Klocke
26 *
27 */
28public class GatewayManager {
29
30	private static Map<Connection,GatewayManager> instances =
31		new HashMap<Connection,GatewayManager>();
32
33	private ServiceDiscoveryManager sdManager;
34
35	private Map<String,Gateway> localGateways = new HashMap<String,Gateway>();
36
37	private Map<String,Gateway> nonLocalGateways = new HashMap<String,Gateway>();
38
39	private Map<String,Gateway> gateways = new HashMap<String,Gateway>();
40
41	private Connection connection;
42
43	private Roster roster;
44
45	private GatewayManager(){
46
47	}
48
49	/**
50	 * Creates a new instance of GatewayManager
51	 * @param connection
52	 * @throws XMPPException
53	 */
54	private GatewayManager(Connection connection) throws XMPPException{
55		this.connection = connection;
56		this.roster = connection.getRoster();
57		sdManager = ServiceDiscoveryManager.getInstanceFor(connection);
58	}
59
60	/**
61	 * Loads all gateways the users server offers
62	 * @throws XMPPException
63	 */
64	private void loadLocalGateways() throws XMPPException{
65		DiscoverItems items = sdManager.discoverItems(connection.getHost());
66		Iterator<Item> iter = items.getItems();
67		while(iter.hasNext()){
68			String itemJID = iter.next().getEntityID();
69			discoverGateway(itemJID);
70		}
71	}
72
73	/**
74	 * Discovers {@link DiscoveryInfo} and {@link DiscoveryInfo.Identity} of a gateway
75	 * and creates a {@link Gateway} object representing this gateway.
76	 * @param itemJID
77	 * @throws XMPPException
78	 */
79	private void discoverGateway(String itemJID) throws XMPPException{
80		DiscoverInfo info = sdManager.discoverInfo(itemJID);
81		Iterator<Identity> i = info.getIdentities();
82
83		while(i.hasNext()){
84			Identity identity = i.next();
85			String category = identity.getCategory();
86			if(category.toLowerCase().equals("gateway")){
87				gateways.put(itemJID, new Gateway(connection,itemJID));
88				if(itemJID.contains(connection.getHost())){
89					localGateways.put(itemJID,
90							new Gateway(connection,itemJID,info,identity));
91				}
92				else{
93					nonLocalGateways.put(itemJID,
94							new Gateway(connection,itemJID,info,identity));
95				}
96				break;
97			}
98		}
99	}
100
101	/**
102	 * Loads all getways which are in the users roster, but are not supplied by the
103	 * users server
104	 * @throws XMPPException
105	 */
106	private void loadNonLocalGateways() throws XMPPException{
107		if(roster!=null){
108			for(RosterEntry entry : roster.getEntries()){
109				if(entry.getUser().equalsIgnoreCase(StringUtils.parseServer(entry.getUser())) &&
110						!entry.getUser().contains(connection.getHost())){
111					discoverGateway(entry.getUser());
112				}
113			}
114		}
115	}
116
117	/**
118	 * Returns an instance of GatewayManager for the given connection. If no instance for
119	 * this connection exists a new one is created and stored in a Map.
120	 * @param connection
121	 * @return an instance of GatewayManager
122	 * @throws XMPPException
123	 */
124	public GatewayManager getInstanceFor(Connection connection) throws XMPPException{
125		synchronized(instances){
126			if(instances.containsKey(connection)){
127				return instances.get(connection);
128			}
129			GatewayManager instance = new GatewayManager(connection);
130			instances.put(connection, instance);
131			return instance;
132		}
133	}
134
135	/**
136	 * Returns a list of gateways which are offered by the users server, wether the
137	 * user is registered to them or not.
138	 * @return a List of Gateways
139	 * @throws XMPPException
140	 */
141	public List<Gateway> getLocalGateways() throws XMPPException{
142		if(localGateways.size()==0){
143			loadLocalGateways();
144		}
145		return new ArrayList<Gateway>(localGateways.values());
146	}
147
148	/**
149	 * Returns a list of gateways the user has in his roster, but which are offered by
150	 * remote servers. But note that this list isn't automatically refreshed. You have to
151	 * refresh is manually if needed.
152	 * @return a list of gateways
153	 * @throws XMPPException
154	 */
155	public List<Gateway> getNonLocalGateways() throws XMPPException{
156		if(nonLocalGateways.size()==0){
157			loadNonLocalGateways();
158		}
159		return new ArrayList<Gateway>(nonLocalGateways.values());
160	}
161
162	/**
163	 * Refreshes the list of gateways offered by remote servers.
164	 * @throws XMPPException
165	 */
166	public void refreshNonLocalGateways() throws XMPPException{
167		loadNonLocalGateways();
168	}
169
170	/**
171	 * Returns a Gateway object for a given JID. Please note that it is not checked if
172	 * the JID belongs to valid gateway. If this JID doesn't belong to valid gateway
173	 * all operations on this Gateway object should fail with a XMPPException. But there is
174	 * no guarantee for that.
175	 * @param entityJID
176	 * @return a Gateway object
177	 */
178	public Gateway getGateway(String entityJID){
179		if(localGateways.containsKey(entityJID)){
180			return localGateways.get(entityJID);
181		}
182		if(nonLocalGateways.containsKey(entityJID)){
183			return nonLocalGateways.get(entityJID);
184		}
185		if(gateways.containsKey(entityJID)){
186			return gateways.get(entityJID);
187		}
188		Gateway gateway = new Gateway(connection,entityJID);
189		if(entityJID.contains(connection.getHost())){
190			localGateways.put(entityJID, gateway);
191		}
192		else{
193			nonLocalGateways.put(entityJID, gateway);
194		}
195		gateways.put(entityJID, gateway);
196		return gateway;
197	}
198
199}
200