1 8 9 package mx4j.tools.adaptor.http; 10 11 import java.io.IOException ; 12 import java.io.InputStream ; 13 import java.io.InterruptedIOException ; 14 import java.io.OutputStream ; 15 import java.net.ServerSocket ; 16 import java.net.Socket ; 17 import java.util.Date ; 18 import java.util.HashMap ; 19 import java.util.Iterator ; 20 import java.util.Map ; 21 import java.util.StringTokenizer ; 22 23 import javax.management.JMException ; 24 import javax.management.MBeanRegistration ; 25 import javax.management.MBeanServer ; 26 import javax.management.MalformedObjectNameException ; 27 import javax.management.ObjectName ; 28 import javax.xml.parsers.DocumentBuilder ; 29 import javax.xml.parsers.DocumentBuilderFactory ; 30 import javax.xml.parsers.ParserConfigurationException ; 31 32 import mx4j.log.Log; 33 import mx4j.log.Logger; 34 import mx4j.tools.adaptor.AdaptorServerSocketFactory; 35 import mx4j.tools.adaptor.PlainAdaptorServerSocketFactory; 36 import mx4j.util.Base64Codec; 37 import org.w3c.dom.Document ; 38 39 44 public class HttpAdaptor implements HttpAdaptorMBean, MBeanRegistration 45 { 46 private static final String VERSION = "3.0.2"; 47 48 51 private int port = 8080; 52 53 56 private String host = "localhost"; 57 58 61 private MBeanServer server; 62 63 66 private ServerSocket serverSocket; 67 68 71 private boolean alive; 72 73 76 private Map commands = new HashMap (); 77 78 81 private ProcessorMBean processor = null; 82 83 86 private ObjectName processorName = null; 87 88 91 private ProcessorMBean defaultProcessor = new DefaultProcessor(); 92 93 private String authenticationMethod = "none"; 94 95 private String realm = "MX4J"; 97 98 private Map authorizations = new HashMap (); 99 100 private AdaptorServerSocketFactory socketFactory = null; 101 102 private ObjectName factoryName; 103 104 private String processorClass; 105 106 private Date startDate; 107 108 private long requestsCount; 109 110 private String [][] defaultCommandProcessors = { 111 {"server", "mx4j.tools.adaptor.http.ServerCommandProcessor"}, 112 {"serverbydomain", "mx4j.tools.adaptor.http.ServerByDomainCommandProcessor"}, 113 {"mbean", "mx4j.tools.adaptor.http.MBeanCommandProcessor"}, 114 {"setattributes", "mx4j.tools.adaptor.http.SetAttributesCommandProcessor"}, 115 {"setattribute", "mx4j.tools.adaptor.http.SetAttributeCommandProcessor"}, 116 {"getattribute", "mx4j.tools.adaptor.http.GetAttributeCommandProcessor"}, 117 {"delete", "mx4j.tools.adaptor.http.DeleteMBeanCommandProcessor"}, 118 {"invoke", "mx4j.tools.adaptor.http.InvokeOperationCommandProcessor"}, 119 {"create", "mx4j.tools.adaptor.http.CreateMBeanCommandProcessor"}, 120 {"constructors", "mx4j.tools.adaptor.http.ConstructorsCommandProcessor"}, 121 {"relation", "mx4j.tools.adaptor.http.RelationCommandProcessor"}, 122 {"empty", "mx4j.tools.adaptor.http.EmptyCommandProcessor"}}; 123 124 private DocumentBuilder builder; 125 126 130 public HttpAdaptor() 131 { 132 } 133 134 149 public HttpAdaptor(int port) 150 { 151 this.port = port; 152 } 153 154 155 170 public HttpAdaptor(String host) 171 { 172 this.host = host; 173 } 174 175 176 192 public HttpAdaptor(int port, String host) 193 { 194 this.port = port; 195 this.host = host; 196 } 197 198 199 204 public void setPort(int port) 205 { 206 if (alive) 207 { 208 throw new IllegalArgumentException ("Not possible to change port with the server running"); 209 } 210 this.port = port; 211 } 212 213 214 219 public int getPort() 220 { 221 return port; 222 } 223 224 225 230 public void setHost(String host) 231 { 232 if (alive) 233 { 234 throw new IllegalArgumentException ("Not possible to change port with the server running"); 235 } 236 this.host = host; 237 } 238 239 240 246 public String getHost() 247 { 248 return host; 249 } 250 251 252 257 public void setAuthenticationMethod(String method) 258 { 259 if (alive) 260 { 261 throw new IllegalArgumentException ("Not possible to change authentication method with the server running"); 262 } 263 if (method == null || !(method.equals("none") || method.equals("basic") || method.equals("digest"))) 264 { 265 throw new IllegalArgumentException ("Only accept methods none/basic/digest"); 266 } 267 this.authenticationMethod = method; 268 } 269 270 271 276 public String getAuthenticationMethod() 277 { 278 return authenticationMethod; 279 } 280 281 282 288 public void setProcessor(ProcessorMBean processor) 289 { 290 this.processor = processor; 291 this.processorName = null; 292 } 293 294 295 303 public void setProcessorClass(String processorClass) 304 { 305 this.processorClass = processorClass; 306 } 307 308 309 315 public void setProcessorNameString(String processorName) throws MalformedObjectNameException 316 { 317 this.processorName = new ObjectName (processorName); 318 } 319 320 321 328 public void setProcessorName(ObjectName processorName) 329 { 330 this.processor = null; 331 this.processorName = processorName; 332 } 333 334 public ProcessorMBean getProcessor() 335 { 336 return this.processor; 337 } 338 339 public ObjectName getProcessorName() 340 { 341 return this.processorName; 342 } 343 344 349 public void setSocketFactory(AdaptorServerSocketFactory factory) 350 { 351 this.factoryName = null; 352 this.socketFactory = factory; 353 } 354 355 356 361 public void setSocketFactoryName(ObjectName factoryName) 362 { 363 this.socketFactory = null; 364 this.factoryName = factoryName; 365 } 366 367 368 373 public void setSocketFactoryNameString(String factoryName) throws MalformedObjectNameException 374 { 375 this.socketFactory = null; 376 this.factoryName = new ObjectName (factoryName); 377 } 378 379 380 385 public boolean isActive() 386 { 387 return alive; 388 } 389 390 391 396 public Date getStartDate() 397 { 398 return startDate; 399 } 400 401 402 407 public long getRequestsCount() 408 { 409 return requestsCount; 410 } 411 412 413 418 public String getVersion() 419 { 420 return VERSION; 421 } 422 423 424 427 public void addCommandProcessor(String path, HttpCommandProcessor processor) 428 { 429 commands.put(path, processor); 430 if (alive) 431 { 432 processor.setMBeanServer(server); 433 processor.setDocumentBuilder(builder); 434 } 435 } 436 437 438 441 public void addCommandProcessor(String path, String processorClass) 442 { 443 try 444 { 445 HttpCommandProcessor processor = (HttpCommandProcessor)Class.forName(processorClass).newInstance(); 446 addCommandProcessor(path, processor); 447 } 448 catch (Exception e) 449 { 450 Logger log = getLogger(); 451 log.error("Exception creating Command Processor of class " + processorClass, e); 452 } 453 } 454 455 456 459 public void removeCommandProcessor(String path) 460 { 461 if (commands.containsKey(path)) 462 { 463 commands.remove(path); 464 } 465 } 466 467 468 471 public void start() 472 throws IOException 473 { 474 final Logger logger = getLogger(); 475 476 if (server != null) 477 { 478 serverSocket = createServerSocket(); 479 480 if (serverSocket == null) 481 { 482 logger.error("Server socket is null"); 483 return; 484 } 485 486 if (processorClass != null && processorName != null) 487 { 488 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Building processor class of type " + processorClass + " and name " + processorName); 489 try 490 { 491 server.createMBean(processorClass, processorName, null); 492 } 493 catch (JMException e) 494 { 495 logger.error("Exception creating processor class", e); 496 } 497 } 498 499 Iterator i = commands.values().iterator(); 500 while (i.hasNext()) 501 { 502 HttpCommandProcessor processor = (HttpCommandProcessor)i.next(); 503 processor.setMBeanServer(server); 504 processor.setDocumentBuilder(builder); 505 } 506 507 if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("HttpAdaptor server listening on port " + port); 508 alive = true; 509 Thread serverThread = new Thread (new Runnable () 510 { 511 public void run() 512 { 513 if (logger.isEnabledFor(Logger.INFO)) logger.info("HttpAdaptor version " + VERSION + " started on port " + port); 514 515 startDate = new Date (); 516 requestsCount = 0; 517 518 while (alive) 519 { 520 try 521 { 522 Socket client = null; 523 client = serverSocket.accept(); 524 if (!alive) 525 { 526 client.close(); 527 break; 528 } 529 requestsCount++; 530 new HttpClient(client).start(); 531 } 532 catch (InterruptedIOException e) 533 { 534 continue; 535 } 536 catch (IOException e) 537 { 538 continue; 539 } 540 catch (Exception e) 541 { 542 logger.warn("Exception during request processing", e); 543 continue; 544 } 545 catch (Error e) 546 { 547 logger.error("Error during request processing", e); 548 continue; 549 } 550 } 551 try 552 { 553 serverSocket.close(); 554 } 555 catch (IOException e) 556 { 557 logger.warn("Exception closing the server", e); 558 } 559 serverSocket = null; 560 alive = false; 561 if (logger.isEnabledFor(Logger.INFO)) logger.info("HttpAdaptor version " + VERSION + " stopped on port " + port); 562 } 563 }); 564 serverThread.start(); 565 } 566 else 567 { 568 if (logger.isEnabledFor(Logger.INFO)) logger.info("Start failed, no server target server has been set"); 569 } 570 } 571 572 573 578 public void restart() 579 throws IOException 580 { 581 stop(); 582 start(); 583 } 584 585 586 589 public void stop() 590 { 591 try 592 { 593 if (alive) 594 { 595 alive = false; 596 new Socket (host, port); 598 } 599 } 600 catch (IOException e) 601 { 602 getLogger().warn(e.getMessage()); 603 } 604 try 605 { 606 if (serverSocket != null) 607 { 608 serverSocket.close(); 609 } 610 } 611 catch (IOException e) 612 { 613 getLogger().warn(e.getMessage()); 614 } 615 } 616 617 618 621 public void addAuthorization(String username, String password) 622 { 623 if (username == null || password == null) 624 { 625 throw new IllegalArgumentException ("username and passwords cannot be null"); 626 } 627 authorizations.put(username, password); 628 } 629 630 631 634 public ObjectName preRegister(MBeanServer server, ObjectName name) 635 throws java.lang.Exception 636 { 637 this.server = server; 638 buildCommands(); 639 return name; 640 } 641 642 643 public void postRegister(Boolean registrationDone) 644 { 645 } 646 647 648 public void preDeregister() 649 throws java.lang.Exception 650 { 651 stop(); 653 } 654 655 656 public void postDeregister() 657 { 658 } 659 660 private Logger getLogger() 661 { 662 return Log.getLogger(getClass().getName()); 663 } 664 665 private ServerSocket createServerSocket() throws IOException 666 { 667 if (socketFactory == null) 668 { 669 if (factoryName == null) 670 { 671 socketFactory = new PlainAdaptorServerSocketFactory(); 672 return socketFactory.createServerSocket(port, 50, host); 673 } 674 else 675 { 676 try 677 { 678 return (ServerSocket )server.invoke(factoryName, "createServerSocket", new Object []{new Integer (port), new Integer (50), host}, new String []{"int", "int", "java.lang.String"}); 679 } 680 catch (Exception x) 681 { 682 Logger log = getLogger(); 683 log.error("Exception invoking AdaptorServerSocketFactory via MBeanServer", x); 684 } 685 } 686 } 687 else 688 { 689 return socketFactory.createServerSocket(port, 50, host); 690 } 691 692 return null; 693 } 694 695 696 private boolean isUsernameValid(String username, String password) 697 { 698 if (authorizations.containsKey(username)) 699 { 700 return password.equals(authorizations.get(username)); 701 } 702 return false; 703 } 704 705 706 protected HttpCommandProcessor getProcessor(String path) 707 { 708 return (HttpCommandProcessor)commands.get(path); 709 } 710 711 712 715 protected void buildCommands() 716 { 717 Logger log = getLogger(); 718 try 719 { 720 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 721 builder = factory.newDocumentBuilder(); 722 for (int i = 0; i < defaultCommandProcessors.length; i++) 723 { 724 try 725 { 726 HttpCommandProcessor processor = (HttpCommandProcessor)Class.forName(defaultCommandProcessors[i][1]).newInstance(); 727 commands.put(defaultCommandProcessors[i][0], processor); 728 } 729 catch (Exception e) 730 { 731 log.warn("Exception building command procesor", e); 732 } 733 } 734 } 735 catch (ParserConfigurationException e) 736 { 737 log.error("Exception building the Document Factories", e); 738 } 739 } 740 741 742 protected void postProcess(HttpOutputStream out, HttpInputStream in, Document document) 743 throws IOException , JMException 744 { 745 boolean processed = false; 746 if (processorName != null) 748 { 749 if (server.isRegistered(processorName) && 750 server.isInstanceOf(processorName, "mx4j.tools.adaptor.http.ProcessorMBean")) 751 { 752 server.invoke(processorName, 753 "writeResponse", 754 new Object []{out, in, document}, 755 new String []{"mx4j.tools.adaptor.http.HttpOutputStream", "mx4j.tools.adaptor.http.HttpInputStream", "org.w3c.dom.Document"}); 756 processed = true; 757 } 758 else 759 { 760 Logger log = getLogger(); 761 if (log.isEnabledFor(Logger.TRACE)) log.trace(processorName + " not found"); 762 } 763 } 764 if (!processed && processor != null) 765 { 766 processor.writeResponse(out, in, document); 767 processed = true; 768 } 769 if (!processed) 770 { 771 defaultProcessor.writeResponse(out, in, document); 772 } 773 } 774 775 776 protected void findUnknownElement(String path, HttpOutputStream out, HttpInputStream in) 777 throws IOException , JMException 778 { 779 boolean processed = false; 780 if (processorName != null) 782 { 783 if (server.isRegistered(processorName) && 784 server.isInstanceOf(processorName, "mx4j.tools.adaptor.http.ProcessorMBean")) 785 { 786 server.invoke(processorName, 787 "notFoundElement", 788 new Object []{path, out, in}, 789 new String []{"java.lang.String", "mx4j.tools.adaptor.http.HttpOutputStream", "mx4j.tools.adaptor.http.HttpInputStream"}); 790 processed = true; 791 } 792 else 793 { 794 Logger log = getLogger(); 795 if (log.isEnabledFor(Logger.TRACE)) log.trace(processorName + " not found"); 796 } 797 } 798 if (!processed && processor != null) 799 { 800 processor.notFoundElement(path, out, in); 801 processed = true; 802 } 803 if (!processed) 804 { 805 defaultProcessor.notFoundElement(path, out, in); 806 } 807 } 808 809 810 protected String preProcess(String path) 811 throws IOException , JMException 812 { 813 boolean processed = false; 814 if (processorName != null) 816 { 817 Logger log = getLogger(); 818 if (log.isEnabledFor(Logger.TRACE)) log.trace("Preprocess using " + processorName); 819 if (server.isRegistered(processorName) && 820 server.isInstanceOf(processorName, "mx4j.tools.adaptor.http.ProcessorMBean")) 821 { 822 if (log.isEnabledFor(Logger.TRACE)) log.trace("Preprocessing"); 823 path = (String )server.invoke(processorName, 824 "preProcess", 825 new Object []{path}, 826 new String []{"java.lang.String"}); 827 processed = true; 828 } 829 else 830 { 831 if (log.isEnabledFor(Logger.TRACE)) log.trace(processorName + " not found"); 832 } 833 } 834 if (!processed && processor != null) 835 { 836 path = processor.preProcess(path); 837 processed = true; 838 } 839 if (!processed) 840 { 841 path = defaultProcessor.preProcess(path); 842 } 843 return path; 844 } 845 846 847 protected void postProcess(HttpOutputStream out, HttpInputStream in, Exception e) 848 throws IOException , JMException 849 { 850 boolean processed = false; 851 if (processorName != null) 853 { 854 if (server.isRegistered(processorName) && 855 server.isInstanceOf(processorName, "mx4j.tools.adaptor.http.ProcessorMBean")) 856 { 857 server.invoke(processorName, 858 "writeError", 859 new Object []{out, in, e}, 860 new String []{"mx4j.tools.adaptor.http.HttpOutputStream", "mx4j.tools.adaptor.http.HttpInputStream", "java.lang.Exception"}); 861 processed = true; 862 } 863 else 864 { 865 Logger log = getLogger(); 866 if (log.isEnabledFor(Logger.TRACE)) log.trace(processorName + " not found"); 867 } 868 } 869 if (!processed && processor != null) 870 { 871 processor.writeError(out, in, e); 872 processed = true; 873 } 874 if (!processed) 875 { 876 defaultProcessor.writeError(out, in, e); 877 } 878 } 879 880 881 private class HttpClient extends Thread 882 { 883 private Socket client; 884 885 886 HttpClient(Socket client) 887 { 888 this.client = client; 889 } 890 891 public boolean isValid(String authorizationString) 892 { 893 if (authenticationMethod.startsWith("basic")) 894 { 895 authorizationString = authorizationString.substring(5, authorizationString.length()); 896 String decodeString = new String (Base64Codec.decodeBase64(authorizationString.getBytes())); 897 if (decodeString.indexOf(":") > 0) 898 { 899 try 900 { 901 StringTokenizer tokens = new StringTokenizer (decodeString, ":"); 902 String username = tokens.nextToken(); 903 String password = tokens.nextToken(); 904 return isUsernameValid(username, password); 905 } 906 catch (Exception e) 907 { 908 return false; 909 } 910 } 911 } 912 return false; 913 } 914 915 916 private boolean handleAuthentication(HttpInputStream in, HttpOutputStream out) throws IOException 917 { 918 if (authenticationMethod.equals("basic")) 919 { 920 String result = in.getHeader("authorization"); 921 if (result != null) 922 { 923 if (isValid(result)) 924 { 925 return true; 926 } 927 throw new HttpException(HttpConstants.STATUS_FORBIDDEN, "Authentication failed"); 928 } 929 930 out.setCode(HttpConstants.STATUS_AUTHENTICATE); 931 out.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\""); 932 out.sendHeaders(); 933 out.flush(); 934 return false; 935 } 936 if (authenticationMethod.equals("digest")) 937 { 938 } 940 return true; 941 } 942 943 944 public void run() 945 { 946 Logger log = getLogger(); 947 HttpInputStream httpIn = null; 948 HttpOutputStream httpOut = null; 949 try 950 { 951 InputStream in = client.getInputStream(); 953 httpIn = new HttpInputStream(in); 954 httpIn.readRequest(); 955 956 String path = httpIn.getPath(); 958 String queryString = httpIn.getQueryString(); 959 if (log.isEnabledFor(Logger.DEBUG)) log.debug("Request " + path + ((queryString == null) ? "" : ("?" + queryString))); 960 String postPath = preProcess(path); 961 if (!postPath.equals(path)) 962 { 963 if (log.isEnabledFor(Logger.DEBUG)) log.debug("Processor replaced path " + path + " with the path " + postPath); 964 path = postPath; 965 } 966 OutputStream out = client.getOutputStream(); 967 httpOut = new HttpOutputStream(out, httpIn); 968 if (!handleAuthentication(httpIn, httpOut)) 969 { 970 return; 971 } 972 HttpCommandProcessor processor = getProcessor(path.substring(1, path.length())); 973 if (processor == null) 974 { 975 if (log.isEnabledFor(Logger.DEBUG)) log.debug("No suitable command processor found, requesting from processor path " + path); 976 findUnknownElement(path, httpOut, httpIn); 977 } 978 else 979 { 980 Document document = processor.executeRequest(httpIn); 981 postProcess(httpOut, httpIn, document); 982 } 983 } 984 catch (Exception ex) 985 { 986 log.warn("Exception during http request", ex); 987 if (httpOut != null) 988 { 989 try 990 { 991 postProcess(httpOut, httpIn, ex); 992 } 993 catch (IOException e) 994 { 995 log.warn("IOException during http request", e); 996 } 997 catch (JMException e) 998 { 999 log.warn("JMException during http request", e); 1000 } 1001 catch (RuntimeException rte) 1002 { 1003 log.error("RuntimeException during http request", rte); 1004 } 1005 catch (Error er) 1006 { 1007 log.error("Error during http request ", er); 1008 } 1009 catch (Throwable t) 1010 { 1011 log.fatal("Throwable during http request ", t); 1012 } 1013 } 1014 } 1015 catch (Error ex) 1016 { 1017 log.error("Error during http request ", ex); 1018 } 1019 finally 1020 { 1021 try 1022 { 1023 if (httpOut != null) 1024 { 1025 httpOut.flush(); 1026 } 1027 } 1028 catch (IOException e) 1029 { 1030 log.warn("Exception during request processing", e); 1031 } 1032 finally 1033 { 1034 try 1035 { 1036 client.close(); 1038 } 1039 catch (IOException e) 1040 { 1041 log.info("Exception during socket close", e); 1042 } 1043 } 1044 } 1045 } 1046 } 1047} 1048 1049 | Popular Tags |