1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package snifos.server;
21
22 import org.apache.commons.digester.Digester;
23
24 import org.apache.log4j.Logger;
25
26 import org.dom4j.Document;
27
28 import org.xml.sax.SAXException;
29
30 import snifos.application.Application;
31 import snifos.application.ApplicationInfo;
32
33 import snifos.common.UserId;
34
35 import snifos.communication.CommModule;
36 import snifos.communication.CommModuleInfo;
37
38 import snifos.exception.ConfigurationException;
39 import snifos.exception.InvalidUserException;
40
41 import java.io.IOException;
42 import java.io.InputStream;
43
44 import java.lang.reflect.Constructor;
45 import java.lang.reflect.InvocationTargetException;
46
47 import java.util.ArrayList;
48 import java.util.Collection;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.Iterator;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Properties;
55 import java.util.WeakHashMap;
56
57
58 /***
59 * Reference implementation of SNIFOS server interface.
60 * @version $Id: SnifosServer.java,v 1.2 2004/05/08 21:55:29 mwerla Exp $
61 */
62 public class SnifosServer implements Server {
63 private static Logger logger = Logger.getLogger(SnifosServer.class);
64 private Map specificApplications = Collections.synchronizedMap(new HashMap());
65 private Map applications = Collections.synchronizedMap(new WeakHashMap());
66 private Map userIdToAppIdMap = Collections.synchronizedMap(new HashMap());
67 private List commModules = new ArrayList();
68 private Collection commModulesConfigurations;
69 private int appIdCounter = 0;
70 private Constructor appClassConstructor;
71 private Collection uniqueInstancesConfigurations;
72 private Properties typicalInstanceConfiguration = null;
73 private boolean wait = false;
74
75 /***
76 * Server constructor.
77 * @param configurationStream Stream from which server configurtion
78 * will be loaded.
79 * @throws ConfigurationException Thrown when configuratin is wrong.
80 */
81 public SnifosServer(InputStream configurationStream)
82 throws ConfigurationException {
83 try {
84 loadConfiguration(configurationStream);
85 } catch (Exception e) {
86 logger.error("Error while loading configuration", e);
87 throw new ConfigurationException(e);
88 }
89
90 createSpecificApplications();
91 createCommModules();
92 }
93
94 /***
95 * Method for unit test purposes only.
96 * @return Wait flag value.
97 */
98 public boolean isWait() {
99 return wait;
100 }
101
102 void setWait() {
103 this.wait = true;
104 }
105
106 private void createCommModules() throws ConfigurationException {
107 synchronized (commModulesConfigurations) {
108 for (Iterator iter = commModulesConfigurations.iterator();
109 iter.hasNext();) {
110 CommModuleInfo modConfig = (CommModuleInfo) iter.next();
111 CommModule commModule = (CommModule) createObject(modConfig.getConstructor());
112 commModule.setServer(this);
113 commModule.configure(modConfig.getProperties(),
114 commModules.size());
115 commModules.add(commModule);
116 }
117 }
118 }
119
120 private void createSpecificApplications() throws ConfigurationException {
121 synchronized (uniqueInstancesConfigurations) {
122 for (Iterator iter = uniqueInstancesConfigurations.iterator();
123 iter.hasNext();) {
124 Properties properties = (Properties) iter.next();
125
126 Application app = createAndConfigureApp(properties, true);
127
128 Integer intAppId = new Integer(appIdCounter++);
129 specificApplications.put(intAppId, app);
130 applications.put(intAppId, app);
131 }
132 }
133 }
134
135 private Object createObject(Constructor constructor)
136 throws ConfigurationException {
137 try {
138 return constructor.newInstance(new Object[0]);
139 } catch (IllegalArgumentException e) {
140 throw new ConfigurationException(e);
141 } catch (InstantiationException e) {
142 throw new ConfigurationException(e);
143 } catch (IllegalAccessException e) {
144 throw new ConfigurationException(e);
145 } catch (InvocationTargetException e) {
146 throw new ConfigurationException(e);
147 }
148 }
149
150 private void loadConfiguration(InputStream configurationStream)
151 throws IOException, SAXException, SecurityException,
152 NoSuchMethodException, ClassNotFoundException {
153 Digester digester = new Digester();
154
155 digester.addObjectCreate("server", ArrayList.class);
156
157 digester.addObjectCreate("server/commModule", CommModuleInfo.class);
158 digester.addSetProperties("server/commModule");
159 addProperties(digester, "server/commModule");
160 digester.addSetNext("server/commModule", "add");
161
162 digester.addObjectCreate("server/application", ApplicationInfo.class);
163 digester.addSetProperties("server/application");
164
165 digester.addObjectCreate("server/application/typicalConfiguration",
166 Properties.class);
167 addProperties(digester, "server/application/typicalConfiguration");
168 digester.addSetNext("server/application/typicalConfiguration",
169 "setTypicalConfiguration");
170
171 digester.addObjectCreate("server/application/uniqueConfiguration",
172 Properties.class);
173 addProperties(digester, "server/application/uniqueConfiguration");
174 digester.addSetNext("server/application/uniqueConfiguration",
175 "addUniqueConfiguration");
176
177 digester.addSetNext("server/application", "add");
178
179 ArrayList result = (ArrayList) digester.parse(configurationStream);
180 ApplicationInfo appInfo = (ApplicationInfo) result.remove(result.size() -
181 1);
182 commModulesConfigurations = result;
183
184 typicalInstanceConfiguration = appInfo.getTypicalConfiguration();
185 uniqueInstancesConfigurations = appInfo.getUniqueConfigurations();
186
187 appClassConstructor = Class.forName(appInfo.getClassName())
188 .getConstructor(new Class[0]);
189 }
190
191 private void addProperties(Digester digester, String node) {
192 String localNode = node + "/property";
193 digester.addCallMethod(localNode, "setProperty", 2);
194 digester.addCallParam(localNode, 0, "name");
195 digester.addCallParam(localNode, 1);
196 }
197
198 /***
199 * @see Server#sendMessage(snifos.common.UserId, org.dom4j.Document)
200 */
201 public void sendMessage(UserId userId, Document message) {
202 CommModule commModule = (CommModule) commModules.get(userId.getCommModuleId());
203
204 try {
205 commModule.sendMessage(userId, message);
206 } catch (InvalidUserException e) {
207 logger.warn(
208 "System unstable - trying to send message to wrong user!");
209 unregisterUser(userId);
210 }
211 }
212
213 /***
214 * @see Server#receiveMessage(UserId, Document)
215 */
216 public void receiveMessage(UserId userId, Document message)
217 throws ConfigurationException, InvalidUserException {
218 Integer appId = (Integer) userIdToAppIdMap.get(userId);
219
220 if (appId == null) {
221 appId = checkApps(userId, message);
222
223 if (appId == null) {
224 if (typicalInstanceConfiguration != null) {
225 appId = createNewApp(userId, message);
226 } else {
227 throw new InvalidUserException();
228 }
229 }
230
231 userIdToAppIdMap.put(userId, appId);
232 }
233
234 Application app = (Application) applications.get(appId);
235 app.receiveMessage(userId, message);
236 }
237
238 private Integer createNewApp(UserId userId, Document message)
239 throws ConfigurationException, InvalidUserException {
240 Application app = createAndConfigureApp(typicalInstanceConfiguration,
241 false);
242 Integer appId = new Integer(appIdCounter++);
243 applications.put(appId, app);
244
245 Thread thread = new Thread(app);
246 thread.start();
247 app.registerUser(userId, message);
248
249 return appId;
250 }
251
252 private Application createAndConfigureApp(Properties configuration,
253 boolean unique) throws ConfigurationException {
254 Application app = (Application) createObject(appClassConstructor);
255 app.setServer(this);
256 app.configure(configuration, unique);
257
258 return app;
259 }
260
261 private Integer checkApps(UserId userId, Document message) {
262 Integer appKey = null;
263 List appKeysList = new ArrayList(applications.keySet());
264 Collections.sort(appKeysList);
265
266 for (Iterator appIter = appKeysList.iterator(); appIter.hasNext();) {
267 appKey = (Integer) appIter.next();
268
269 Application app = (Application) applications.get(appKey);
270
271 try {
272 app.registerUser(userId, message);
273
274 break;
275 } catch (InvalidUserException e) {
276 appKey = null;
277 }
278 }
279
280 return appKey;
281 }
282
283 /***
284 * @see snifos.server.Server#disconnectUser(UserId)
285 */
286 public void disconnectUser(UserId userId) {
287 CommModule commModule = (CommModule) commModules.get(userId.getCommModuleId());
288 commModule.disconnectUser(userId);
289 userIdToAppIdMap.remove(userId);
290 }
291
292 /***
293 * @see snifos.server.Server#unregisterUser(UserId)
294 */
295 public void unregisterUser(UserId userId) {
296 Integer appId = (Integer) userIdToAppIdMap.get(userId);
297 Application app = (Application) applications.get(appId);
298
299 if (app != null) {
300 try {
301 app.unregisterUser(userId);
302 } catch (InvalidUserException e) {
303
304 }
305
306 userIdToAppIdMap.remove(userId);
307 }
308 }
309
310 /***
311 * @see Runnable#run()
312 */
313 public void run() {
314 synchronized (applications) {
315 for (Iterator iter = applications.values().iterator();
316 iter.hasNext();) {
317 Application app = (Application) iter.next();
318
319 Thread thread = new Thread(app);
320 thread.start();
321 }
322 }
323
324 int commModulesCounter = 0;
325
326 synchronized (commModules) {
327 for (Iterator iter = commModules.iterator(); iter.hasNext();) {
328 CommModule commModule = (CommModule) iter.next();
329
330 Thread thread = new Thread(commModule);
331 thread.start();
332 commModulesCounter++;
333 }
334 }
335
336
337 if (wait) {
338 synchronized (this) {
339 while (commModulesCounter > 0) {
340 try {
341 wait();
342 commModulesCounter--;
343 } catch (InterruptedException e) {
344 e.printStackTrace();
345 }
346 }
347 }
348 }
349 }
350 }