View Javadoc

1   /*
2   Copyright (C) 2007 Dirk Huenniger
3   
4   This library is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Lesser General Public
6   License as published by the Free Software Foundation; either
7   version 2.1 of the License, or (at your option) any later version.
8   
9   This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  Lesser General Public License for more details.
13  
14  You should have received a copy of the GNU Lesser General Public
15  License along with this library; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17   */
18  
19  package org.indi.server;
20  
21  import java.io.IOException;
22  import java.net.URL;
23  import java.nio.channels.ClosedChannelException;
24  import java.nio.channels.SelectableChannel;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Enumeration;
28  import java.util.HashSet;
29  import java.util.PropertyResourceBundle;
30  import java.util.Queue;
31  import java.util.Set;
32  import java.util.concurrent.LinkedBlockingQueue;
33  
34  import javax.xml.parsers.ParserConfigurationException;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.indi.clientmessages.GetProperties;
39  import org.indi.objects.TransferType;
40  import org.indi.objects.Vector;
41  import org.indi.reactor.EventHandler;
42  import org.indi.reactor.EventHandlerFactory;
43  import org.indi.reactor.Reactor;
44  import org.xml.sax.SAXException;
45  
46  /**
47   * An Indiserver to host drivers
48   * 
49   * @author Dirk Hünniger
50   * 
51   */
52  public class IndiServer {
53  
54      private final Log log = LogFactory.getLog(IndiServer.class);
55      /**
56       * A collection of devicedrivers hosted by this server
57       */
58      public Collection<BasicDevice> drivers;
59      /**
60       * A collection of clients connected to this server
61       */
62      public Collection<ClientHandler> clientHandlers;
63      /**
64       * the reactor used by this server for event dispatching
65       */
66      public Reactor reactor;
67      /**
68       * the version of the indisever
69       */
70      public String version = "1.0";
71      /**
72       * the set of registered oberservers observing changes of a particular
73       * property of a particular driver
74       */
75      public Set<Observer> observers;
76  
77      /**
78       * class constructor
79       * 
80       * @throws IOException
81       */
82      public IndiServer() throws IOException {
83  	this.observers = new HashSet<Observer>();
84  	this.reactor = new Reactor();
85  	Queue<Object> toDriverQueue = new LinkedBlockingQueue<Object>();
86  	this.drivers = new ArrayList<BasicDevice>();
87  	this.reactor.register(new Dispatcher(toDriverQueue, this));
88  	Acceptor a = new Acceptor(this.reactor, new EventHandlerFactory() {
89  	    public EventHandler produce(Reactor r, SelectableChannel ch)
90  		    throws ClosedChannelException, IOException {
91  		try {
92  		    return new ClientHandler(r, ch);
93  		} catch (ParserConfigurationException e) {
94  		    e.printStackTrace();
95  		} catch (SAXException e) {
96  		    e.printStackTrace();
97  		}
98  		return null;
99  	    }
100 	}, 7624, toDriverQueue);
101 	this.clientHandlers = a.getClientHandlers();
102     }
103 
104     /**
105      * add a new driver to the server
106      * 
107      * @param driver
108      *                the driver to be added
109      */
110     public void addDriver(BasicDevice driver) {
111 	this.drivers.add(driver);
112     }
113 
114     /**
115      * add a new observer to the server
116      * 
117      * @param o
118      *                the observer to be added
119      */
120     public void addObserver(Observer o) {
121 	this.observers.add(o);
122 	String dev = o.getDevice();
123 	GetProperties gp = new GetProperties(this.version, dev);
124 	for (Device d : this.drivers) {
125 	    String name = d.getName();
126 	    if (name == null) {
127 		continue;
128 	    }
129 	    if (name.equals(dev)) {
130 		d.onGetProperties(gp);
131 	    }
132 	}
133     }
134 
135     /**
136      * remove an existing obeserver from the server.
137      * 
138      * @param o
139      *                the observer to be removed
140      */
141     public void removeObserver(Observer o) {
142 	this.observers.remove(o);
143     }
144 
145     /**
146      * send an indiobject to all interested clients
147      * 
148      * @param object
149      *                the indiobject to be send
150      * @param type
151      *                the way it should be send
152      * @param message
153      *                the message to be sent along with the object
154      */
155     public void sendToClients(org.indi.objects.Object object,
156 	    TransferType type, String message) {
157 	for (ClientHandler ch : this.clientHandlers) {
158 	    ch.send(object, type, message);
159 	}
160 	if (object instanceof Vector) {
161 	    Vector vector = (Vector) object;
162 	    vector.setTransferType(type);
163 	    for (Observer o : this.observers) {
164 		if (vector.getDevice().equals(o.getDevice())) {
165 		    if (o.getName().equals(vector.getName())
166 			    || vector.getTransferType() == TransferType.Del) {
167 			if ((vector.getTransferType() == TransferType.Set)
168 				&& o.getState() == ObserverState.State) {
169 			    if (o.laststate == vector.getState()) {
170 				return;
171 			    } else {
172 				o.laststate = vector.getState();
173 			    }
174 			}
175 			o.onObserved(vector);
176 		    }
177 		}
178 		;
179 
180 	    }
181 	}
182     }
183 
184     /**
185      * 
186      * @param args
187      */
188     public static void main(String[] args) {
189 	try {
190 	    new IndiServer().startServer(args);
191 	} catch (IOException e) {
192 	    e.printStackTrace();
193 	}
194     }
195 
196     /**
197      * 
198      * @param args
199      */
200     private void startServer(String[] args) {
201 	try {
202 	    ArrayList<String> deveiceToStart = new ArrayList<String>();
203 	    nameScanForDevicesToStart(args, deveiceToStart);
204 	    classNameScanForDevicesToStart(args, deveiceToStart);
205 	    if (!startAtLeastOneDevice(deveiceToStart)) {
206 		this.log.error("no drivers successfully started!");
207 	    } else {
208 		while (true) {
209 		    this.reactor.handleEvents(10);
210 		}
211 	    }
212 	} catch (IOException e) {
213 	    this.log.error("could not start indi server due to exception", e);
214 	}
215     }
216 
217     private void classNameScanForDevicesToStart(String[] args,
218 	    ArrayList<String> devicesToStart) {
219 	for (String element : args) {
220 	    if (element.length() > 0) {
221 		try {
222 		    Class driverClass = Thread.currentThread()
223 			    .getContextClassLoader().loadClass(element);
224 		    if (driverClass.isAssignableFrom(BasicDevice.class)) {
225 			devicesToStart.add(element);
226 		    } else {
227 			this.log
228 				.error("argument is not a driver or driver class ("
229 					+ element + ")");
230 		    }
231 		} catch (ClassNotFoundException e) {
232 		    this.log.error("argument is not a driver class (" + element
233 			    + ")", e);
234 		}
235 	    }
236 	}
237     }
238 
239     private void nameScanForDevicesToStart(String[] args,
240 	    ArrayList<String> driversToStart) throws IOException {
241 	Enumeration<URL> driverresources = Thread.currentThread()
242 		.getContextClassLoader().getResources("META-INF/indi");
243 	while (driverresources.hasMoreElements()) {
244 	    URL url = driverresources.nextElement();
245 	    PropertyResourceBundle bundle = new PropertyResourceBundle(url
246 		    .openStream());
247 	    Enumeration<String> keys = bundle.getKeys();
248 	    while (keys.hasMoreElements()) {
249 		String driverClassName = keys.nextElement();
250 		String driverName = bundle.getString(driverClassName);
251 		boolean driverActivated = false;
252 		for (int index = 0; index < args.length; index++) {
253 		    if (args[index].equals(driverName)) {
254 			driversToStart.add(driverClassName);
255 			driverActivated = true;
256 			args[index] = "";
257 		    }
258 		}
259 		if (!driverActivated) {
260 		    this.log.info("detected deacivated driver for "
261 			    + driverName + " (" + driverClassName + ")");
262 		} else {
263 		    this.log.info("detected acivated driver for " + driverName
264 			    + " (" + driverClassName + ")");
265 		}
266 	    }
267 	}
268     }
269 
270     /**
271      * 
272      * @param driversToStart
273      * @return
274      */
275     private boolean startAtLeastOneDevice(ArrayList<String> driversToStart) {
276 	boolean atLeastOnDriverStarted = false;
277 	for (String driverClassName : driversToStart) {
278 	    try {
279 		Class driverClass = Thread.currentThread()
280 			.getContextClassLoader().loadClass(driverClassName);
281 		addDriver((BasicDevice) driverClass.getConstructor(
282 			IndiServer.class).newInstance(this));
283 		this.log.info("Successfuly started driver class "
284 			+ driverClassName);
285 		atLeastOnDriverStarted = true;
286 	    } catch (Exception e) {
287 		this.log.error("could not instanciate Driver: "
288 			+ driverClassName + " due to exception", e);
289 	    }
290 	}
291 	return atLeastOnDriverStarted;
292     }
293 }