View Javadoc

1   package twcsckernel.serverKernel.utils;
2   
3   import java.io.IOException;
4   import java.rmi.NoSuchObjectException;
5   import java.rmi.Remote;
6   import java.rmi.RemoteException;
7   import java.rmi.server.RMISocketFactory;
8   import java.rmi.server.UnicastRemoteObject;
9   import java.util.HashMap;
10  import java.util.HashSet;
11  import java.util.Map;
12  import java.util.Set;
13  
14  import twcsckernel.projectbase.common.RemoteAgent;
15  import twcsckernel.projectbase.common.RemoteServer;
16  import twcsckernel.projectbase.rmi.registry.LocateRegistry;
17  import twcsckernel.projectbase.rmi.registry.RegistryImpl;
18  import twcsckernel.projectbase.rmi.socketfactory.ServerTwoWaySocketFactory;
19  import twcsckernel.serverKernel.impl.UserImpl;
20  import twcsckernel.serverKernel.usr.UserGarbageCollector;
21  
22  /***
23   * Manager serwera RMI służący do odpalania serwera RMI wraz z przydzieleniem
24   * serwerowi i rejestracją podanej implementacji
25   * {@link twcsckernel.projectbase.common.RemoteServer RemoteServer}.<br/>
26   * Ponadto wyłącznie ta klasa powinna być używana do rejestrowania i
27   * wyrejestrowywania obiektów zdalnych z serwera RMI za pomocą metod
28   * {@link #exportObject(Remote) exportObject} oraz
29   * {@link #unexportObject(Remote, boolean) unexportObject}
30   * 
31   * @author VMD Group
32   * 
33   */
34  public class RmiManager {
35  
36  	private RegistryImpl registry = null;
37  
38  	private Map<RemoteAgent, Integer> registeredAgentMap = new HashMap<RemoteAgent, Integer>();
39  
40  	private Set<UserImpl> registeredUserSet = new HashSet<UserImpl>();
41  
42  	private UserGarbageCollector userGbC = null;
43  
44  	private int port;
45  
46  	private long gbcInterval;
47  
48  	/***
49  	 * Konstruktor rejestrujący odpowiednią fabrykę gniazd sieciowych.
50  	 * 
51  	 * @param port -
52  	 *            numer portu na którym będzie odpalany serwer RMI
53  	 * @param useUserGbC -
54  	 *            <code>true</code> jeśli ma być włączony user garbage
55  	 *            collector lub <code>false</code> w przeciwnym wypadku
56  	 * @param connectionTimeout -
57  	 *            timeout (w ms) próby połączenia do klienta (np. jeśli jest
58  	 *            za NAT/firewall). Jeśli zero, to traktowany jest jak 
59  	 *            nieskończoność i połączenie będzie nawiązywane dopóki nie
60  	 *            wystąpi błąd połączenia (wtedy dopiero natstępuje połączenie
61  	 *            pasywne).
62  	 * @throws IOException -
63  	 *             wyjątek rzucany w przypadku niepowodzenia rejestracji fabryki
64  	 *             gniazd sieciowych.
65  	 */
66  	public RmiManager(int port, boolean useUserGbC, int connectionTimeout)
67  			throws IOException {
68  		RMISocketFactory.setSocketFactory(new ServerTwoWaySocketFactory(
69  				connectionTimeout));
70  		this.port = port;
71  		if (useUserGbC) {
72  			userGbC = new UserGarbageCollector();
73  			userGbC.setUserSet(registeredUserSet);
74  		}
75  	}
76  
77  	/***
78  	 * @return interwał czasu (w milisekunach) co jaki ma być "sprzątane" - zero
79  	 *         oznacza wyłączony user garbage collector
80  	 */
81  	public long getUserCollectorInterval() {
82  		return gbcInterval;
83  	}
84  
85  	/***
86  	 * Ustawia "sprzątacza" użytkowników, którzy nie utrzymywali połączenia
87  	 * przez pewnien określony w nim czas.
88  	 * 
89  	 * @param interval -
90  	 *            interwał czasu (w milisekunach) co jaki ma być "sprzątane" -
91  	 *            zero oznacza wyłączenie
92  	 */
93  	public void setUserCollectorInterval(long interval) {
94  		if (userGbC != null) {
95  			userGbC.setInterval(interval);
96  			gbcInterval = interval;
97  		}
98  	}
99  
100 	/***
101 	 * Metoda odpala serwer na danym porcie oraz rejestruje podany serwer
102 	 * logowania
103 	 * {@link twcsckernel.projectbase.common.RemoteServer RemoteServer} na
104 	 * serwerze RMI.
105 	 * 
106 	 * @param remoteServer -
107 	 *            serwer logowania rejestrowany w serwerze RMI
108 	 * @throws RemoteException -
109 	 *             wyjątek rzucany w przypadku niepowodzenia utworzenia serwera
110 	 *             RMI lub niepowodzenia rejestracji na nim serwera logowania
111 	 */
112 	public void startRegistryServer(RemoteServer remoteServer)
113 			throws RemoteException {
114 		registry = (RegistryImpl) LocateRegistry.createRegistry(port);
115 		try {
116 			UnicastRemoteObject.exportObject(remoteServer, port);
117 		} catch (RemoteException re) {
118 			UnicastRemoteObject.unexportObject(registry, true);
119 			registry = null;
120 			throw re;
121 		}
122 		registry.registerRemoteServer(remoteServer);
123 		if (userGbC != null)
124 			userGbC.enable();
125 	}
126 
127 	/***
128 	 * Metoda wyłączająca serwer RMI.
129 	 * 
130 	 * @return <code>true</code> wtedy i tylko wtedy gdy serwer był włączony i
131 	 *         operacja wyłączenia się powiodła lub <code>false</code> w
132 	 *         przeciwnym wypadku.
133 	 */
134 	public boolean stopRegistryServer() {
135 		if (registry == null)
136 			return false;
137 		try {
138 			if (userGbC != null)
139 				userGbC.disable();
140 			synchronized (registeredUserSet) {
141 				// wykonywana jest kopia ze wzgledu na to, ze
142 				// user.kickUser() modyfikuje posrednio
143 				// registeredUserSet
144 				UserImpl[] tmpUsrCopy = registeredUserSet
145 						.toArray(new UserImpl[0]);
146 				for (UserImpl user : tmpUsrCopy)
147 					user.kickUser();
148 				return UnicastRemoteObject.unexportObject(registry, true);
149 			}
150 		} catch (NoSuchObjectException e) {
151 			return false;
152 		}
153 	}
154 
155 	/***
156 	 * Metoda rejestrująca obiekt zdalny w serwerze RMI. Tylko ona powinna być
157 	 * używana do rejestrowania obiektów zdalnych.<br/> Metoda wyróżnia dwie
158 	 * klasy na które reaguje oddzielnie:
159 	 * <ul>
160 	 * <li><i>UserImpl</i> - metoda zapisuje dodatkowo obiekt w zbiorze
161 	 * dostępnym dla garbage collectora użytkowników, przez co podlegają jego
162 	 * ew. kontroli.</li>
163 	 * <li><i>RemoteAgent</i> - obiekty te można rejestrować wielokrotnie,
164 	 * gdyż pluginy mogą potencjalnie stosować tego samego agenta dla wielu
165 	 * użytkowników. Prowadzony jest zatem dla każdego zarejestrowanego agenta
166 	 * licznik liczby rejestrowań. Obiekt niezarejestrowany jest rejestrowany i
167 	 * dodawany do mapy liczników. Jeśli ponownie ten sam agent jest
168 	 * rejestrowany zwiększany jest tylko licznik rejestrowań.<br/> Obrazowy
169 	 * schemat:<br/> <code>
170 	 * if (agent.count==0) {
171 	 *   rejestruj(agent);
172 	 *   agent.count=1;
173 	 * } else
174 	 *   agent.count++;
175 	 * </code></li>
176 	 * </ul>
177 	 * 
178 	 * 
179 	 * @param object -
180 	 *            rejestrowany obiekt
181 	 * @throws RemoteException -
182 	 *             wyjątek rzucany w przypadku niepowodzenia rejestracji
183 	 */
184 	public void exportObject(Remote object) throws RemoteException {
185 		if (object instanceof RemoteAgent)
186 			synchronized (registeredAgentMap) {
187 				Integer existing = registeredAgentMap.get(object);
188 				if (existing == null) {
189 					existing = 1;
190 					UnicastRemoteObject.exportObject(object, port);
191 					registeredAgentMap.put((RemoteAgent) object, existing);
192 				} else {
193 					existing++;
194 					registeredAgentMap.put((RemoteAgent) object, existing);
195 				}
196 			}
197 		else if (object instanceof UserImpl)
198 			synchronized (registeredUserSet) {
199 				UnicastRemoteObject.exportObject(object, port);
200 				registeredUserSet.add((UserImpl) object);
201 			}
202 		else
203 			UnicastRemoteObject.exportObject(object, port);
204 
205 	}
206 
207 	/***
208 	 * Metoda wyrejestrowująca obiekt zdalny z serwera RMI. Tylko ta metoda
209 	 * powinna być używana do wyrejestrowania obiektu.<br/> Metoda wyróżnia
210 	 * dwie klasy na które reaguje oddzielnie:
211 	 * <ul>
212 	 * <li><i>UserImpl</i> - metoda usuwa dodatkowo obiekt ze zbioru
213 	 * dostępnego dla garbage collectora użytkowników</li>
214 	 * <li><i>RemoteAgent</i> - działa odwrotnie do
215 	 * {@link #exportObject(Remote) exportObject}.<br/> Obrazowy schemat:<br/>
216 	 * <code>
217 	 * if (agent.count>1)
218 	 *   agent.count--;
219 	 * else {
220 	 *   wyrejestruj(agent);
221 	 *   agent.count=0;
222 	 * }
223 	 * </code>
224 	 * </li>
225 	 * </ul>
226 	 * 
227 	 * @param object -
228 	 *            obiekt to wyrejestrowania
229 	 * @param ifForce -
230 	 *            jeśli <code>true</code> to obiekt zostanie wyrejestrowany
231 	 *            nawet w przypadku przeprowadzanych w aktualnie operacji
232 	 *            zdalnych, w przeciwnym wypadku obiekt będzie wyrejestrowany
233 	 *            tylko jeśli nie wykonuje żadnych operacji zdalnych.
234 	 * @return <code>true</code> jeśli operacja zakończona powodzeniem lub
235 	 *         <code>false</code> w przeciwnym wypadku.
236 	 * @throws NoSuchObjectException -
237 	 *             wyjątek rzucany jeśli podany obiekt nie był zarejestrowany
238 	 */
239 	public boolean unexportObject(Remote object, boolean ifForce)
240 			throws NoSuchObjectException {
241 		if (object instanceof RemoteAgent)
242 			synchronized (registeredAgentMap) {
243 				Integer existing = registeredAgentMap.get(object);
244 				if (existing == null)
245 					return false; // jak ktos rejestrowal poza rmiManager to
246 				// niech
247 				// sie sam martwi
248 				if (existing > 1) {
249 					existing--;
250 					registeredAgentMap.put((RemoteAgent) object, existing);
251 					return true;
252 				} else {
253 					boolean result = UnicastRemoteObject.unexportObject(object,
254 							ifForce);
255 					if (result)
256 						registeredAgentMap.remove(object);
257 					return result;
258 				}
259 			}
260 		else if (object instanceof UserImpl)
261 			synchronized (registeredUserSet) {
262 				boolean result = UnicastRemoteObject.unexportObject(object,
263 						ifForce);
264 				if (result)
265 					registeredUserSet.remove(object);
266 				return result;
267 			}
268 		else
269 			return UnicastRemoteObject.unexportObject(object, ifForce);
270 	}
271 }