blob: 82c59d03b5a0103b40204dfe63299c4cd9db4e8b [file] [log] [blame]
Stephane Frenotcb0356f2006-07-20 09:36:47 +00001/*
2 * Copyright (C) MX4J.
3 * All rights reserved.
4 *
5 * This software is distributed under the terms of the MX4J License version 1.0.
6 * See the terms of the MX4J License in the documentation provided with this software.
7 */
8/*
9 * Copyright 2005 The Apache Software Foundation
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 *
23 */
24package org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http;
25
26import java.io.IOException;
27import java.io.InputStream;
28import java.io.InterruptedIOException;
29import java.io.OutputStream;
30import java.net.ServerSocket;
31import java.net.Socket;
32import java.util.Date;
33import java.util.HashMap;
34import java.util.Iterator;
35import java.util.Map;
36import java.util.StringTokenizer;
37import javax.management.JMException;
38import javax.management.MBeanRegistration;
39import javax.management.MBeanServer;
40import javax.management.MalformedObjectNameException;
41import javax.management.ObjectName;
42import javax.xml.parsers.DocumentBuilder;
43import javax.xml.parsers.DocumentBuilderFactory;
44import javax.xml.parsers.ParserConfigurationException;
45
46import org.w3c.dom.Document;
47
48import org.osgi.framework.ServiceReference;
49import org.osgi.service.log.LogService;
50
51import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.AdaptorServerSocketFactory;
52import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.PlainAdaptorServerSocketFactory;
53
54import org.apache.felix.mosgi.jmx.agent.mx4j.util.Base64Codec;
55
56import org.apache.felix.mosgi.jmx.httpconnector.HttpConnectorActivator;
57
Stephane Frenot2854c4a2006-07-20 13:55:52 +000058import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.ServerCommandProcessor;
59import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.ServerByDomainCommandProcessor;
60import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.MBeanCommandProcessor;
61import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.SetAttributesCommandProcessor;
62import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.DeleteMBeanCommandProcessor;
63import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.InvokeOperationCommandProcessor;
64import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.CreateMBeanCommandProcessor;
65import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.ConstructorsCommandProcessor;
66import org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.EmptyCommandProcessor;
67
Stephane Frenotcb0356f2006-07-20 09:36:47 +000068/**
69 * HttpAdaptor sets the basic adaptor listening for HTTP requests
70 *
71 * @author <a href="mailto:tibu@users.sourceforge.net">Carlos Quiroz</a>
72 * @version $Revision: 1.1.1.1 $
73 */
74public class HttpAdaptor implements HttpAdaptorMBean, MBeanRegistration
75{
76 private static final String VERSION = "2.0 Beta 1";
77
78 /** Port to listen for connections */
79 private int port = 8080;
80
81 /** Host where to set the server socket */
82 private String host = "localhost";
83
84 /** Target server */
85 private MBeanServer server;
86
87 /** Server socket */
88 private ServerSocket serverSocket;
89
90 /** Indicates whether the server is running */
91 private boolean alive;
92
93 /** Map of commands indexed by the request path */
94 private Map commands = new HashMap();
95
96 /** Target processor */
97 private ProcessorMBean processor = null;
98
99 /** Target processor name */
100 private ObjectName processorName = null;
101
102 /** Default processor */
103 private ProcessorMBean defaultProcessor = new DefaultProcessor();
104
105 private String authenticationMethod = "none";
106
107 // Should be dependant on the server?
108 private String realm="MX4J";
109
110 private Map authorizations = new HashMap();
111
112 private AdaptorServerSocketFactory socketFactory = null;
113
114 private ObjectName factoryName;
115
116 private String processorClass;
117
118 private Date startDate;
119
120 private long requestsCount;
121
122 private String[][] defaultCommandProcessors = {
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000123 {"server", ServerCommandProcessor.class.getName()},
124 {"serverbydomain", ServerByDomainCommandProcessor.class.getName()},
125 {"mbean", MBeanCommandProcessor.class.getName()},
126 {"setattributes", SetAttributesCommandProcessor.class.getName()},
127 {"setattribute", SetAttributeCommandProcessor.class.getName()},
128 {"getattribute", GetAttributeCommandProcessor.class.getName()},
129 {"delete", DeleteMBeanCommandProcessor.class.getName()},
130 {"invoke", InvokeOperationCommandProcessor.class.getName()},
131 {"create", CreateMBeanCommandProcessor.class.getName()},
132 {"constructors", ConstructorsCommandProcessor.class.getName()},
133 {"empty", EmptyCommandProcessor.class.getName()}};
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000134
135// {"relation", "mx4j.tools.adaptor.http.RelationCommandProcessor"},
136
137 private DocumentBuilder builder;
138
139 /**
140 * Default Constructor added so that we can have some additional
141 * constructors as well.
142 */
143 public HttpAdaptor()
144 {
145 }
146
147 /**
148 * Overloaded constructor to allow the port to be set.
149 * The reason this was added was to allow the loading of this adaptor by
150 * the dynamic loading service of the MBean server and have the port set
151 * from a param in the mlet file. Example: (replaced lt & gt symbol with [])
152 * <br>[mlet code="mx4j.tools.adaptor.http.HttpAdaptor"
153 * <br> archive="mx4j.jar"
154 * <br> name="Server:name=HttpAdaptor"]
155 * <br> [arg type="int" value="12345"]
156 * <br>[/mlet]
157 *
158 * <p>This constructor uses the default host or the host must be set later.
159 * @param port The port on which the HttpAdaptor should listen
160 */
161 public HttpAdaptor(int port)
162 {
163 this.port = port;
164 }
165
166
167 /**
168 * Overloaded constructor to allow the host to be set.
169 * The reason this was added was to allow the loading of this adaptor by
170 * the dynamic loading service of the MBean server and have the host set
171 * from a param in the mlet file. Example: (replaced lt & gt symbol with [])
172 * <br>[mlet code="mx4j.tools.adaptor.http.HttpAdaptor"
173 * <br> archive="mx4j.jar"
174 * <br> name="Server:name=HttpAdaptor"]
175 * <br> [arg type="java.lang.String" value="someserver.somehost.com"]
176 * <br>[/mlet]
177 *
178 * <p>This constructor uses the default port or the port must be set later.
179 * @param host The host on which the HttpAdaptor should listen
180 */
181 public HttpAdaptor(String host)
182 {
183 this.host = host;
184 }
185
186
187 /**
188 * Overloaded constructor to allow the port to be set.
189 * The reason this was added was to allow the loading of this adaptor by
190 * the dynamic loading service of the MBean server and have the port set
191 * from a param in the mlet file. Example: (replaced lt & gt symbol with [])
192 * NOTE that the port must come before the host in the arg list of the mlet
193 * <br>[mlet code="mx4j.tools.adaptor.http.HttpAdaptor"
194 * <br> archive="mx4j.jar"
195 * <br> name="Server:name=HttpAdaptor"]
196 * <br> [arg type="int" value="12345"]
197 * <br> [arg type="java.lang.String" value="someserver.somehost.com"]
198 * <br>[/mlet]
199 *
200 * @param port The port on which the HttpAdaptor should listen
201 * @param host The host on which the HttpAdaptor should listen
202 */
203 public HttpAdaptor(int port, String host)
204 {
205 this.port = port;
206 this.host = host;
207 }
208
209
210 /**
211 * Sets the value of the server's port
212 *
213 * @param port the new port's value
214 */
215 public void setPort(int port)
216 {
217 if (alive)
218 {
219 throw new IllegalArgumentException("Not possible to change port with the server running");
220 }
221 this.port = port;
222 }
223
224
225 /**
226 * Returns the port where the server is running on. Default is 8080
227 *
228 * @return HTTPServer's port
229 */
230 public int getPort()
231 {
232 return port;
233 }
234
235
236 /**
237 * Sets the host name where the server will be listening
238 *
239 * @param host Server's host
240 */
241 public void setHost(String host)
242 {
243 if (alive)
244 {
245 throw new IllegalArgumentException("Not possible to change port with the server running");
246 }
247 this.host = host;
248 }
249
250
251 /**
252 * Return the host name the server will be listening to. If null the server
253 * listen at the localhost
254 *
255 * @return the current hostname
256 */
257 public String getHost()
258 {
259 return host;
260 }
261
262
263 /**
264 * Sets the Authentication Method.
265 *
266 * @param method none/basic/digest
267 */
268 public void setAuthenticationMethod(String method) {
269 if (alive)
270 {
271 throw new IllegalArgumentException("Not possible to change authentication method with the server running");
272 }
273 if (method == null || !(method.equals("none") || method.equals("basic") || method.equals("digest")))
274 {
275 throw new IllegalArgumentException("Only accept methods none/basic/digest");
276 }
277 this.authenticationMethod = method;
278 }
279
280
281 /**
282 * Authentication Method
283 *
284 * @return authentication method
285 */
286 public String getAuthenticationMethod()
287 {
288 return authenticationMethod;
289 }
290
291
292 /**
293 * Sets the object which will post process the XML results. The last value set
294 * between the setPostProcessor and setPostProcessorName will be the valid one
295 *
296 * @param processor a Post processor object
297 */
298 public void setProcessor(ProcessorMBean processor)
299 {
300 this.processor = processor;
301 this.processorName = null;
302 }
303
304
305 /**
306 * Sets the classname of the object which will post process the XML results. The adaptor
307 * will try to build the object and use the processor name ObjectName to register it
308 * The class name has to implements mx4j.tools.adaptor.http.ProcessorMBean and be MBean
309 * compliant
310 * @param processorClass a Post processor object
311 */
312 public void setProcessorClass(String processorClass)
313 {
314 this.processorClass = processorClass;
315 }
316
317
318 /**
319 * Sets the object name of the PostProcessor MBean. If ProcessorClass is set the processor
320 * will be created
321 * @param processorName a Post processor object
322 */
323 public void setProcessorNameString(String processorName) throws MalformedObjectNameException
324 {
325 this.processorName = new ObjectName(processorName);
326 }
327
328
329 /**
330 * Sets the object name which will post process the XML result. The last value
331 * set between the setPostProcessor and setPostProcessorName will be the valid
332 * one. The MBean will be verified to be of instance HttpPostProcessor
333 * @param processorName The new processorName value
334 */
335 public void setProcessorName(ObjectName processorName)
336 {
337 this.processor = null;
338 this.processorName = processorName;
339 }
340
341 public ProcessorMBean getProcessor()
342 {
343 return this.processor;
344 }
345
346 public ObjectName getProcessorName()
347 {
348 return this.processorName;
349 }
350
351 /**
352 * Sets the object which create the server sockets
353 *
354 * @param factory the socket factory
355 */
356 public void setSocketFactory(AdaptorServerSocketFactory factory)
357 {
358 this.factoryName = null;
359 this.socketFactory = factory;
360 }
361
362
363 /**
364 * Sets the factory's object name which will create the server sockets
365 *
366 * @param factoryName the socket factory
367 */
368 public void setSocketFactoryName(ObjectName factoryName)
369 {
370 this.socketFactory = null;
371 this.factoryName = factoryName;
372 }
373
374
375 /**
376 * Sets the factory's object name which will create the server sockets
377 *
378 * @param factoryName the socket factory
379 */
380 public void setSocketFactoryNameString(String factoryName) throws MalformedObjectNameException
381 {
382 this.socketFactory = null;
383 this.factoryName = new ObjectName(factoryName);
384 }
385
386
387 /**
388 * Indicates whether the server's running
389 *
390 * @return The active value
391 */
392 public boolean isActive()
393 {
394 return alive;
395 }
396
397
398 /**
399 * Starting date
400 *
401 * @return The date when the server was started
402 */
403 public Date getStartDate()
404 {
405 return startDate;
406 }
407
408
409 /**
410 * Requests count
411 *
412 * @return The total of requests served so far
413 */
414 public long getRequestsCount()
415 {
416 return requestsCount;
417 }
418
419
420 /**
421 * Gets the HttpAdaptor version
422 *
423 * @return HttpAdaptor's version
424 */
425 public String getVersion()
426 {
427 return VERSION;
428 }
429
430
431 /**
432 * Adds a command processor object
433 */
434 public void addCommandProcessor(String path, HttpCommandProcessor processor)
435 {
436 commands.put(path, processor);
437 if (alive)
438 {
439 processor.setMBeanServer(server);
440 processor.setDocumentBuilder(builder);
441 }
442 }
443
444
445 /**
446 * Adds a command processor object by class
447 */
448 public void addCommandProcessor(String path, String processorClass)
449 {
450 try
451 {
452 HttpCommandProcessor processor = (HttpCommandProcessor)Class.forName(processorClass).newInstance();
453 addCommandProcessor(path, processor);
454 }
455 catch (Exception e)
456 {
457 HttpAdaptor.log(LogService.LOG_ERROR,
458 "Exception creating Command Processor of class " + processorClass,
459 e);
460 }
461 }
462
463
464 /**
465 * Removes a command processor object by class
466 */
467 public void removeCommandProcessor(String path)
468 {
469 if (commands.containsKey(path))
470 {
471 commands.remove(path);
472 }
473 }
474
475
476 /**
477 * Starts the server
478 */
479 public void start()
480 throws IOException
481 {
482
483 if (server != null)
484 {
485 serverSocket = createServerSocket();
486
487 if (serverSocket == null)
488 {
489 HttpAdaptor.log(LogService.LOG_ERROR, "Server socket is null", null);
490 return;
491 }
492
493 if (processorClass != null && processorName != null)
494 {
495 HttpAdaptor.log(LogService.LOG_INFO,"Building processor class of type " + processorClass + " and name " + processorName, null);
496 try {
497 server.createMBean(processorClass, processorName, null);
498 } catch (JMException e) {
499 HttpAdaptor.log(LogService.LOG_INFO, "Exception creating processor class", e);
500 }
501 }
502
503 Iterator i = commands.values().iterator();
504 while (i.hasNext())
505 {
506 HttpCommandProcessor processor = (HttpCommandProcessor)i.next();
507 processor.setMBeanServer(server);
508 processor.setDocumentBuilder(builder);
509 }
510
511 HttpAdaptor.log(LogService.LOG_INFO, "HttpAdaptor server listening on port " + port, null);
512 alive = true;
513 Thread serverThread = new Thread(
514 new Runnable() {
515 public void run() {
516 HttpAdaptor.log(LogService.LOG_INFO, "HttpAdaptor version " + VERSION + " started",null);
517
518 startDate = new Date();
519 requestsCount = 0;
520
521 while (alive)
522 {
523 try
524 {
525 Socket client = null;
526 client = serverSocket.accept();
527 if (!alive)
528 {
529 break;
530 }
531 requestsCount++;
532 new HttpClient(client).start();
533 }
534 catch (InterruptedIOException e)
535 {
536 continue;
537 }
538 catch (IOException e)
539 {
540 continue;
541 }
542 catch (Exception e)
543 {
544 HttpAdaptor.log(LogService.LOG_WARNING, "Exception during request processing", e);
545 continue;
546 }
547 catch (Error e)
548 {
549 HttpAdaptor.log(LogService.LOG_ERROR, "Error during request processing", e);
550 continue;
551 }
552 }
553 try {
554 serverSocket.close();
555 } catch (IOException e) {
556 HttpAdaptor.log(LogService.LOG_WARNING, "Exception closing the server", e);
557 }
558 serverSocket = null;
559 alive = false;
560 HttpAdaptor.log(LogService.LOG_INFO, "Server stopped", null);
561 }
562 });
563 serverThread.start();
564 }
565 else
566 {
567 HttpAdaptor.log(LogService.LOG_INFO,"Start failed, no server target server has been set",null);
568 }
569 }
570
571
572 /**
573 * Restarts the server. Useful when changing the Server parameters
574 *
575 * @deprecated as of RC 1
576 */
577 public void restart()
578 throws IOException
579 {
580 stop();
581 start();
582 }
583
584
585 /**
586 * Stops the HTTP daemon
587 */
588 public void stop()
589 {
590 try
591 {
592 if (alive)
593 {
594 alive = false;
595 // force the close with a socket call
596 new Socket(host, port);
597 }
598 }
599 catch (IOException e)
600 {
601 HttpAdaptor.log(LogService.LOG_WARNING,e.getMessage(),e);
602 }
603 try {
604 if (serverSocket != null)
605 {
606 serverSocket.close();
607 }
608 }
609 catch (IOException e)
610 {
611 HttpAdaptor.log(LogService.LOG_WARNING,e.getMessage(),e);
612 }
613 }
614
615
616 /**
617 * Adds an authorization pair as username/password
618 */
619 public void addAuthorization(String username, String password)
620 {
621 if (username == null || password == null)
622 {
623 throw new IllegalArgumentException("username and passwords cannot be null");
624 }
625 authorizations.put(username, password);
626 }
627
628
629 /**
630 * Gathers some basic data
631 */
632 public ObjectName preRegister(MBeanServer server, ObjectName name)
633 throws java.lang.Exception
634 {
635 this.server = server;
636 buildCommands();
637 return name;
638 }
639
640
641 public void postRegister(Boolean registrationDone) { }
642
643
644 public void preDeregister()
645 throws java.lang.Exception
646 {
647 // stop the server
648 stop();
649 }
650
651 public void postDeregister() { }
652
653 private ServerSocket createServerSocket() throws IOException
654 {
655 if (socketFactory == null)
656 {
657 if (factoryName == null)
658 {
659 socketFactory = new PlainAdaptorServerSocketFactory();
660 return socketFactory.createServerSocket(port, 50, host);
661 }
662 else
663 {
664 try
665 {
666 return (ServerSocket)server.invoke(factoryName, "createServerSocket", new Object[] {new Integer(port), new Integer(50), host}, new String[] {"int", "int", "java.lang.String"});
667 }
668 catch (Exception x)
669 {
670 HttpAdaptor.log(LogService.LOG_ERROR,"Exception invoking AdaptorServerSocketFactory via MBeanServer", x);
671 }
672 }
673 }
674 else
675 {
676 return socketFactory.createServerSocket(port, 50, host);
677 }
678
679 return null;
680 }
681
682
683 private boolean isUsernameValid(String username, String password)
684 {
685 if (authorizations.containsKey(username))
686 {
687 return password.equals(authorizations.get(username));
688 }
689 return false;
690 }
691
692
693 protected HttpCommandProcessor getProcessor(String path)
694 {
695 return (HttpCommandProcessor)commands.get(path);
696 }
697
698
699 /**
700 * Build the commands
701 */
702 protected void buildCommands()
703 {
704 try
705 {
706 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
707 builder = factory.newDocumentBuilder();
708 for (int i=0;i<defaultCommandProcessors.length;i++)
709 {
710 try
711 {
712 HttpCommandProcessor processor = (HttpCommandProcessor)Class.forName(defaultCommandProcessors[i][1]).newInstance();
713 commands.put(defaultCommandProcessors[i][0], processor);
714 } catch (Exception e) {
715 HttpAdaptor.log(LogService.LOG_WARNING,"Exception building command procesor", e);
716 }
717 }
718 }
719 catch (ParserConfigurationException e)
720 {
721 HttpAdaptor.log(LogService.LOG_ERROR,"Exception building the Document Factories", e);
722 }
723 }
724
725
726 protected void postProcess(HttpOutputStream out, HttpInputStream in, Document document)
727 throws IOException, JMException
728 {
729 boolean processed = false;
730 // inefficient but handles modifications at runtime
731 if (processorName != null)
732 {
733 if (server.isRegistered(processorName) &&
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000734 server.isInstanceOf(processorName, ProcessorMBean.class.getName()))
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000735 {
736 server.invoke(processorName,
737 "writeResponse",
738 new Object[]{out, in, document},
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000739 new String[]{HttpOutputStream.class.getName(), HttpInputStream.class.getName(), Document.class.getName()});
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000740 processed = true;
741 }
742 else
743 {
744 HttpAdaptor.log(LogService.LOG_DEBUG,processorName + " not found",null);
745 }
746 }
747 if (!processed && processor != null)
748 {
749 processor.writeResponse(out, in, document);
750 processed = true;
751 }
752 if (!processed)
753 {
754 defaultProcessor.writeResponse(out, in, document);
755 }
756 }
757
758
759 protected void findUnknownElement(String path, HttpOutputStream out, HttpInputStream in)
760 throws IOException, JMException
761 {
762 boolean processed = false;
763 // inefficient but handles modifications at runtime
764 if (processorName != null)
765 {
766 if (server.isRegistered(processorName) &&
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000767 server.isInstanceOf(processorName, ProcessorMBean.class.getName()))
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000768 {
769 server.invoke(processorName,
770 "notFoundElement",
771 new Object[]{path, out, in},
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000772 new String[]{String.class.getName(), HttpOutputStream.class.getName(), HttpInputStream.class.getName()});
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000773 processed = true;
774 }
775 else
776 {
777 HttpAdaptor.log(LogService.LOG_DEBUG,processorName + " not found",null);
778 }
779 }
780 if (!processed && processor != null)
781 {
782 processor.notFoundElement(path, out, in);
783 processed = true;
784 }
785 if (!processed)
786 {
787 defaultProcessor.notFoundElement(path, out, in);
788 }
789 }
790
791
792 protected String preProcess(String path)
793 throws IOException, JMException
794 {
795 boolean processed = false;
796 // inefficient but handles modifications at runtime
797 if (processorName != null)
798 {
799 HttpAdaptor. log(LogService.LOG_DEBUG,"Preprocess using " + processorName,null);
800 if (server.isRegistered(processorName) &&
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000801 server.isInstanceOf(processorName, ProcessorMBean.class.getName()))
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000802 {
803 HttpAdaptor.log(LogService.LOG_DEBUG,"Preprocessing",null);
804 path = (String)server.invoke(processorName,
805 "preProcess",
806 new Object[]{path},
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000807 new String[]{String.class.getName()});
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000808 processed = true;
809 }
810 else
811 {
812 HttpAdaptor.log(LogService.LOG_DEBUG,processorName + " not found",null);
813 }
814 }
815 if (!processed && processor != null)
816 {
817 path = processor.preProcess(path);
818 processed = true;
819 }
820 if (!processed)
821 {
822 path = defaultProcessor.preProcess(path);
823 }
824 return path;
825 }
826
827
828 protected void postProcess(HttpOutputStream out, HttpInputStream in, Exception e)
829 throws IOException, JMException
830 {
831 boolean processed = false;
832 // inefficient but handles modifications at runtime
833 if (processorName != null)
834 {
835 if (server.isRegistered(processorName) &&
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000836 server.isInstanceOf(processorName, ProcessorMBean.class.getName()))
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000837 {
838 server.invoke(processorName,
839 "writeError",
840 new Object[]{out, in, e},
Stephane Frenot2854c4a2006-07-20 13:55:52 +0000841 new String[]{HttpOutputStream.class.getName(),HttpInputStream.class.getName(), Exception.class.getName()});
Stephane Frenotcb0356f2006-07-20 09:36:47 +0000842 processed = true;
843 }
844 else
845 {
846 HttpAdaptor.log(LogService.LOG_DEBUG,processorName + " not found",null);
847 }
848 }
849 if (!processed && processor != null)
850 {
851 processor.writeError(out, in, e);
852 processed = true;
853 }
854 if (!processed)
855 {
856 defaultProcessor.writeError(out, in, e);
857 }
858 }
859
860
861 private class HttpClient extends Thread
862 {
863 private Socket client;
864
865
866 HttpClient(Socket client)
867 {
868 this.client = client;
869 }
870
871 public boolean isValid(String authorizationString) {
872 if (authenticationMethod.startsWith("basic"))
873 {
874 authorizationString = authorizationString.substring(5,authorizationString.length());
875 String decodeString = new String(Base64Codec.decodeBase64(authorizationString.getBytes()));
876 if (decodeString.indexOf(":")>0)
877 {
878 try
879 {
880 StringTokenizer tokens = new StringTokenizer(decodeString, ":");
881 String username = tokens.nextToken();
882 String password = tokens.nextToken();
883 return isUsernameValid(username, password);
884 }
885 catch (Exception e)
886 {
887 return false;
888 }
889 }
890 }
891 return false;
892 }
893
894
895 private boolean handleAuthentication(HttpInputStream in, HttpOutputStream out) throws IOException {
896 if (authenticationMethod.equals("basic"))
897 {
898 String result = in.getHeader("authorization");
899 if (result != null)
900 {
901 if (isValid(result))
902 {
903 return true;
904 }
905 throw new HttpException(HttpConstants.STATUS_FORBIDDEN, "Authentication failed");
906 }
907
908 out.setCode(HttpConstants.STATUS_AUTHENTICATE);
909 out.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\"");
910 out.sendHeaders();
911 out.flush();
912 return false;
913 }
914 if (authenticationMethod.equals("digest"))
915 {
916 // not implemented
917 }
918 return true;
919 }
920
921
922 public void run()
923 {
924 HttpInputStream httpIn = null;
925 HttpOutputStream httpOut = null;
926 try
927 {
928 // get input streams
929 InputStream in = client.getInputStream();
930 httpIn = new HttpInputStream(in);
931 httpIn.readRequest();
932
933 // Find a suitable command processor
934 String path = httpIn.getPath();
935 String queryString = httpIn.getQueryString();
936 HttpAdaptor.log(LogService.LOG_INFO,"Request " + path + ((queryString == null) ? "" : ("?" + queryString)), null);
937 String postPath = preProcess(path);
938 if (!postPath.equals(path))
939 {
940 HttpAdaptor.log(LogService.LOG_INFO,"Processor replaced path " + path + " with the path " + postPath,null);
941 path = postPath;
942 }
943 OutputStream out = client.getOutputStream();
944 httpOut = new HttpOutputStream(out, httpIn);
945 if (!handleAuthentication(httpIn, httpOut)) {
946 return;
947 }
948 HttpCommandProcessor processor = getProcessor(path.substring(1, path.length()));
949 if (processor == null)
950 {
951 HttpAdaptor.log(LogService.LOG_INFO,"No suitable command processor found, requesting from processor path " + path, null);
952 findUnknownElement(path, httpOut, httpIn);
953 }
954 else
955 {
956 Document document = processor.executeRequest(httpIn);
957 postProcess(httpOut, httpIn, document);
958 }
959 }
960 catch (Exception ex)
961 {
962 ex.printStackTrace();
963 HttpAdaptor.log(LogService.LOG_WARNING,"Exception during http request", ex);
964 if (httpOut != null)
965 {
966 try
967 {
968 postProcess(httpOut, httpIn, ex);
969 }
970 catch (IOException e)
971 {
972 HttpAdaptor.log(LogService.LOG_WARNING,"IOException during http request", e);
973 }
974 catch (JMException e)
975 {
976 HttpAdaptor.log(LogService.LOG_WARNING,"JMException during http request", e);
977 }
978 catch (RuntimeException rte)
979 {
980 HttpAdaptor.log(LogService.LOG_ERROR,"RuntimeException during http request", rte);
981 }
982 catch (Error er)
983 {
984 HttpAdaptor.log(LogService.LOG_ERROR,"Error during http request ", er);
985 }
986 catch (Throwable t)
987 {
988 HttpAdaptor.log(LogService.LOG_ERROR,"Throwable during http request ", t);
989 }
990 }
991 }
992 catch (Error ex)
993 {
994 HttpAdaptor.log(LogService.LOG_ERROR,"Error during http request ", ex);
995 }
996 finally
997 {
998 try
999 {
1000 if (httpOut != null)
1001 {
1002 httpOut.flush();
1003 }
1004 // always close the socket
1005 client.close();
1006 }
1007 catch (IOException e)
1008 {
1009 HttpAdaptor.log(LogService.LOG_WARNING,"Exception during request processing", e);
1010 }
1011 }
1012 }
1013 }
1014
1015
1016 private static void log(int prio, String message, Throwable t){
1017 if (HttpConnectorActivator.bc!=null){
1018 ServiceReference logSR=HttpConnectorActivator.bc.getServiceReference(LogService.class.getName());
1019 if (logSR!=null){
1020 ((LogService)HttpConnectorActivator.bc.getService(logSR)).log(prio, message, t);
1021 }else{
1022 System.out.println("No Log Service");
1023 }
1024 }else{
Stephane Frenot2854c4a2006-07-20 13:55:52 +00001025 System.out.println(HttpAdaptor.class.getName()+".log: No bundleContext");
Stephane Frenotcb0356f2006-07-20 09:36:47 +00001026 }
1027 }
1028}
1029