001 /* 002 Copyright (C) 2002-2003 Renaud Pawlak <renaud@aopsys.com>, 003 Laurent Martelli <laurent@aopsys.com> 004 005 This program is free software; you can redistribute it and/or modify 006 it under the terms of the GNU Lesser General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This program is distributed in the hope that it will be useful, but 011 WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 Lesser General Public License for more details. 014 015 You should have received a copy of the GNU Lesser General Public 016 License along with this program; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 018 USA */ 019 020 package org.objectweb.jac.aspects.gui.web; 021 022 import java.io.IOException; 023 import java.io.InputStream; 024 import java.io.OutputStream; 025 import java.io.OutputStreamWriter; 026 import java.io.Writer; 027 import java.util.Arrays; 028 import java.util.Collection; 029 import java.util.HashSet; 030 import java.util.Hashtable; 031 import java.util.Iterator; 032 import java.util.Map; 033 import javax.servlet.http.HttpServletResponse; 034 import org.apache.log4j.Logger; 035 import org.mortbay.http.HttpContext; 036 import org.mortbay.http.HttpServer; 037 import org.mortbay.http.handler.ResourceHandler; 038 import org.mortbay.jetty.servlet.ServletHandler; 039 import org.mortbay.util.InetAddrPort; 040 import org.mortbay.util.MultiException; 041 import org.objectweb.jac.aspects.gui.*; 042 import org.objectweb.jac.aspects.session.SessionAC; 043 import org.objectweb.jac.core.Collaboration; 044 import org.objectweb.jac.core.rtti.AbstractMethodItem; 045 import org.objectweb.jac.lib.Attachment; 046 import org.objectweb.jac.util.ExtArrays; 047 import org.objectweb.jac.util.ExtBoolean; 048 import org.objectweb.jac.util.Strings; 049 050 /** 051 * This class provides a server for web clients using a thin client 052 * protocol and implements the <code>Display</code> interface to 053 * assume data inputs and outputs between JAC objects and web clients. 054 * 055 * <p>This inferface is typically used by Java web clients and more 056 * specifically by servlets. The implementation provided by JAC is 057 * <code>JacServlet</code>. 058 * 059 * @author <a href="mailto:renaud@cnam.fr">Renaud Pawlak</a> 060 * @author <a href="mailto:laurent@aopsys.com">Laurent Martelli</a> 061 */ 062 public class WebDisplay implements CustomizedDisplay { 063 static Logger logger = Logger.getLogger("display"); 064 static Logger loggerWeb = Logger.getLogger("web"); 065 static Logger loggerViews = Logger.getLogger("web.views"); 066 static Logger loggerRequest = Logger.getLogger("web.request"); 067 static Logger loggerHtml = Logger.getLogger("web.html"); 068 static Logger loggerEditor = Logger.getLogger("gui.editor"); 069 070 071 ViewFactory factory; 072 // customizedID -> customized 073 Hashtable frames = new Hashtable(); 074 075 /* The session */ 076 Session session; 077 078 /** 079 * Create a new web display 080 * @param factory the ViewFactory of the display 081 * @param sessionId the sessionId of the display 082 */ 083 public WebDisplay(ViewFactory factory, String sessionId) { 084 this.factory = factory; 085 session = new Session(sessionId); 086 Collaboration.get().addAttribute( 087 SessionAC.SESSION_ID, sessionId ); 088 } 089 090 public static final String RESPONSE = "gui.web.response"; 091 public static final String REQUEST = "gui.web.request"; 092 public static final String ON_ENTER_ACTION = "GuiAC.web.ON_ENTER_ACTION"; 093 094 /** 095 * Set the servlet response for the current collaboration 096 * @param response the servlet response object 097 */ 098 public static void setResponse(HttpServletResponse response) { 099 Collaboration.get().addAttribute(RESPONSE,response); 100 } 101 /** 102 * Returnsx the servlet response of the current collaboration 103 * @return the servlet response of the current collaboration 104 */ 105 public static HttpServletResponse getResponse() { 106 return (HttpServletResponse)Collaboration.get().getAttribute(RESPONSE); 107 } 108 109 /** 110 * Sets the current JacRequest in the context. 111 * @param request the JacRequets 112 */ 113 public static void setRequest(JacRequest request) { 114 loggerRequest.debug("Setting request for "+Strings.hex(Collaboration.get())+ 115 ": "+request); 116 Collaboration.get().addAttribute(REQUEST,request); 117 } 118 119 /** 120 * Returns the current JacRequest contained in the context. 121 * @return the current JacRequest contained in the context 122 */ 123 public static JacRequest getRequest() { 124 return (JacRequest)Collaboration.get().getAttribute(REQUEST); 125 } 126 127 protected static void grabResponse() { 128 setResponse(null); 129 //getRequest().setResponse(); 130 //setResponse(null); 131 } 132 133 /** 134 * Read values of field editors from http request 135 * @param editors field editors for which to read the value 136 * @param request the http request where to read values from 137 * @param commit if true, commit() will be called on the field 138 * editors after readValue() 139 * @see HTMLEditor#readValue(Object) 140 * @see HTMLEditor#commit() 141 */ 142 public static void readValues(DisplayContext editors, JacRequest request, 143 boolean commit) 144 { 145 loggerEditor.debug("reading values from context "+editors); 146 Iterator i = editors.getEditors().iterator(); 147 while (i.hasNext()) { 148 View editor = (View)i.next(); 149 try { 150 Object parameter = request.getParameter(editor.getLabel()); 151 loggerEditor.debug("reading value for "+editor.getLabel()); 152 if (editor instanceof JacRequestReader) { 153 ((JacRequestReader)editor).readValue(request); 154 } else { 155 if (((FieldEditor)editor).isEnabled()) { 156 HTMLEditor htmlEditor = (HTMLEditor)editor; 157 if (htmlEditor.readValue(parameter) && commit) { 158 try { 159 htmlEditor.commit(); 160 } catch (Exception e) { 161 throw new CommitException( 162 e, 163 ((FieldEditor)editor).getSubstance(), 164 ((FieldEditor)editor).getField()); 165 } 166 } 167 } 168 } 169 } catch (CommitException e) { 170 throw e; 171 } catch(Exception e) { 172 loggerEditor.error("Failed to readValue for "+editor+"/"+editor.getLabel(),e); 173 } 174 } 175 } 176 177 public static void readValuesAndRefresh( 178 DisplayContext editors, JacRequest request, 179 boolean commit) 180 { 181 try { 182 readValues(editors,request,commit); 183 editors.getDisplay().refresh(); 184 } catch(CommitException e) { 185 editors.getDisplay().showError( 186 "Commit error", 187 "Failed to set value of "+e.getField()+ 188 " on "+GuiAC.toString(e.getObject())+ 189 ": "+e.getNested().getMessage()); 190 } 191 } 192 193 public void fullRefresh() { 194 Iterator it = frames.entrySet().iterator(); 195 while(it.hasNext()) { 196 Map.Entry entry = (Map.Entry)it.next(); 197 View frame = (View)entry.getValue(); 198 CustomizedGUI customized = ((CustomizedView)frame).getCustomizedGUI(); 199 frame.close(true); 200 View newframe = factory.createView( 201 customized.getTitle(),"Customized", 202 new Object[] {customized,null}, 203 new DisplayContext(this,null)); 204 logger.debug("frame created "+newframe); 205 frames.put(entry.getKey(),newframe); 206 session.newRequest(new Request(newframe)); 207 } 208 } 209 210 public void showCustomized(String id, Object object) { 211 showCustomized(id,object,null); 212 } 213 214 public void showCustomized(String id, Object object, Map panels) { 215 logger.debug("showCustomized("+id+","+object+")"); 216 try { 217 CustomizedGUI customized = (CustomizedGUI)object; 218 if (frames.get(id)!=null) { 219 if (panels!=null) { 220 CustomizedView frame = (CustomizedView)frames.get(id); 221 GenericFactory.initCustomized(frame.getFactory(), 222 frame.getContext(), 223 frame.getPanelView(), 224 customized, panels); 225 } 226 refresh(); 227 } else { 228 HTMLViewer frame = (HTMLViewer)factory.createView( 229 customized.getTitle(),"Customized", 230 new Object[] {customized,panels}, 231 new DisplayContext(this,null)); 232 logger.debug("frame created"); 233 frames.put(id,frame); 234 session.newRequest(new Request((View)frame)); 235 refresh(); 236 } 237 } catch(Exception e) { 238 e.printStackTrace(); 239 showError("showCustomized error",e.toString()); 240 } 241 } 242 243 public void show(Object object) { 244 logger.debug("show("+object+")"); 245 show(object,false); 246 } 247 248 public void show(Object object, 249 String viewType, Object[] viewParams) { 250 logger.debug("show("+object+","+viewType+","+ 251 (viewParams!=null?Arrays.asList(viewParams):null)+")"); 252 show(object,viewType,viewParams,false); 253 } 254 255 protected void show(Object object, boolean newWindow) { 256 show(object, 257 "Object",new String[] {GuiAC.DEFAULT_VIEW}, 258 newWindow); 259 } 260 261 protected void show(Object object, 262 String viewType, Object[] viewParams, 263 boolean newWindow) { 264 if (object==null) { 265 refresh(); 266 /* 267 } else if (object instanceof Throwable) { 268 showError("Exception",object.toString()); 269 ((Throwable)object).printStackTrace(); 270 */ 271 } else if (object instanceof InputStream) { 272 try { 273 byte[] buffer = new byte[4096]; 274 InputStream input = (InputStream)object; 275 int length; 276 OutputStream output = getResponse().getOutputStream(); 277 while ((length=input.read(buffer))!=-1) { 278 output.write(buffer,0,length); 279 } 280 } catch (IOException e) { 281 logger.error("Failed to output stream",e); 282 } finally { 283 getRequest().setResponse(); 284 } 285 } else if (object instanceof Attachment) { 286 try { 287 DisplayContext context = new DisplayContext(this,null); 288 HttpServletResponse response = getResponse(); 289 response.setContentType(((Attachment)object).getMimeType()); 290 response.getOutputStream().write(((Attachment)object).getData()); 291 } catch (IOException e) { 292 logger.error("Failed to output stream",e); 293 } finally { 294 getRequest().setResponse(); 295 } 296 } else { 297 DisplayContext context = new DisplayContext(this,null); 298 View objectView = 299 factory.createView("object", 300 viewType,ExtArrays.add(object,viewParams), 301 context); 302 String title; 303 if (object!=null) { 304 Class substance_type = object.getClass(); 305 String tn = substance_type.getName(); 306 307 title = tn.substring( tn.lastIndexOf('.') + 1) + " " + 308 GuiAC.toString(object); 309 } else { 310 title= "<null>"; 311 } 312 313 View page = factory.createView( 314 title,"Window", 315 new Object[] {objectView,ExtBoolean.valueOf(newWindow)}, 316 context); 317 context.setWindow(page); 318 session.newRequest(new Request(page)); 319 refresh(); 320 } 321 } 322 323 public void openView(Object object) { 324 logger.debug("openView("+object+")"); 325 show(object,true); 326 } 327 328 public boolean showModal(Object object, String title, String header, 329 Object parent, 330 boolean okButton, boolean cancelButton, 331 boolean closeButton) 332 { 333 logger.debug("showModal("+object+","+title+")"); 334 DisplayContext context = new DisplayContext(this,null); 335 logger.debug("new context "+context); 336 View objectView = factory.createObjectView("object",object,context); 337 338 return showModal(objectView,context, 339 title,header,parent, 340 okButton,cancelButton,closeButton); 341 } 342 343 public boolean showModal(Object object, 344 String viewType, Object[] viewParams, 345 String title, String header, 346 Object parent, 347 boolean okButton, boolean cancelButton, 348 boolean closeButton) 349 { 350 logger.debug("showModal("+object+","+title+","+viewType+")"); 351 DisplayContext context = new DisplayContext(this,null); 352 View objectView = factory.createView("object",viewType, 353 ExtArrays.add(object,viewParams),context); 354 355 return showModal(objectView,context, 356 title,header,parent, 357 okButton,cancelButton,closeButton); 358 } 359 360 public boolean showModal(View objectView, DisplayContext context, 361 String title, String header, 362 Object parent, 363 boolean okButton, boolean cancelButton, 364 boolean closeButton) 365 { 366 logger.debug("objectView = "+objectView+", context="+Strings.hex(context)); 367 if (objectView instanceof org.objectweb.jac.aspects.gui.EditorContainer) 368 ((org.objectweb.jac.aspects.gui.EditorContainer)objectView).setShowButtons(false); 369 370 DialogView page = 371 (DialogView)factory.createView( 372 title,"Dialog", 373 new Object[] {objectView,parent,title,header}, 374 context); 375 context.setWindow(page); 376 session.newRequest(new Request(page)); 377 refresh(); 378 379 try { 380 boolean result = page.waitForClose(); 381 WebDisplay.setResponse(((Dialog)page).getResponse()); 382 WebDisplay.setRequest(((Dialog)page).getRequest()); 383 return result; 384 } catch (TimeoutException timeout) { 385 addTimedoutDialog(page); 386 throw timeout; 387 } 388 } 389 390 public boolean showInput(Object substance, AbstractMethodItem method, 391 Object[] parameters) 392 { 393 logger.debug("showInput("+substance+","+method+","+ 394 Arrays.asList(parameters)+")"); 395 DisplayContext dc = (DisplayContext)Collaboration.get() 396 .getAttribute(GuiAC.DISPLAY_CONTEXT); 397 //if (dc==null) { 398 dc = new DisplayContext(this,null); 399 //} 400 DialogView page = GenericFactory.createInputDialog(substance, 401 method,parameters,dc); 402 dc.setWindow(page); 403 session.newRequest(new Request(page)); 404 refresh(); 405 try { 406 if (page.waitForClose()) { 407 EditorContainer inputView = (EditorContainer)page.getContentView(); 408 Iterator it = inputView.getEditors().iterator(); 409 int i=0; 410 JacRequest request = getRequest(); 411 while (it.hasNext()) { 412 if (method.getParameterTypes()[i] != DisplayContext.class) { 413 FieldEditor editor = (FieldEditor)it.next(); 414 method.setParameter(parameters,i,editor.getValue()); 415 } 416 i++; 417 } 418 setResponse(((Dialog)page).getResponse()); 419 setRequest(((Dialog)page).getRequest()); 420 return true; 421 } else { 422 setResponse(((Dialog)page).getResponse()); 423 setRequest(((Dialog)page).getRequest()); 424 return false; 425 } 426 } catch (TimeoutException timeout) { 427 addTimedoutDialog(page); 428 throw timeout; 429 } 430 } 431 432 public boolean showMessage(String message, String title, 433 boolean okButton, 434 boolean cancelButton, 435 boolean closeButton ) { 436 logger.debug("showMessage("+message+","+title+")"); 437 return showModal(null,title,message,null,okButton,cancelButton,closeButton); 438 } 439 440 protected View buildMessage(String title, String message) { 441 DisplayContext context=(DisplayContext)Collaboration.get() 442 .getAttribute(GuiAC.DISPLAY_CONTEXT); 443 if(context==null) { 444 context=new DisplayContext(this,null); 445 } 446 View label = factory.createView("message","text", 447 new Object[] {message,null,null},context); 448 View page = factory.createView("Object view","Window", 449 new Object[] {label,Boolean.FALSE},context); 450 return page; 451 } 452 453 public void showMessage(String title, String message) { 454 logger.debug("showMessage("+title+","+message+")"); 455 try { 456 View page = buildMessage(title,message); 457 session.newRequest(new Request(page)); 458 } finally { 459 refresh(); 460 } 461 } 462 463 public Object showRefreshMessage(String title, String message) { 464 View page = null; 465 try { 466 DisplayContext context=(DisplayContext)Collaboration.get() 467 .getAttribute(GuiAC.DISPLAY_CONTEXT); 468 if (context==null) { 469 context=new DisplayContext(this,null); 470 } 471 View label = factory.createView("message","text", 472 new Object[] {message,null,null},context); 473 page = factory.createView("Object view","RefreshWindow", 474 new Object[] {label},context); 475 session.newRequest(new Request(page)); 476 } finally { 477 refresh(); 478 } 479 return page; 480 } 481 482 public void showError(String title, String message) { 483 showMessage(title,message); 484 } 485 486 public void showStatus(String message) { 487 } 488 489 public void applicationStarted() { 490 } 491 492 493 public CustomizedView getCustomizedView(String customizedID) { 494 return (CustomizedView)frames.get(customizedID); 495 } 496 public Collection getCustomizedViews() { 497 return frames.values(); 498 } 499 public ViewFactory getFactory() { 500 return factory; 501 } 502 503 String displayID; 504 505 public String getDisplayID() { 506 return displayID; 507 } 508 509 public void setDisplayID(String displayID) { 510 this.displayID = displayID; 511 } 512 513 public void close() { 514 // close all customized guis 515 Iterator i = frames.values().iterator(); 516 while(i.hasNext()) { 517 View view = (View)i.next(); 518 view.close(true); 519 } 520 } 521 522 // definition of AbstractServer abstract methods 523 524 public Object[] buildParameterValues(AbstractMethodItem method, String[] params) { 525 Object[] result = new Object[params.length]; 526 Class[] pts = method.getParameterTypes(); 527 for(int i=0; i<params.length; i++) 528 { 529 // HTMLEditor editor = getEditorComponent(null,method,i); 530 // result[i] = editor.getValue(params[i]); 531 } 532 return result; 533 } 534 535 public void refresh() { 536 if (getResponse()==null) { 537 logger.debug("refresh ignored since response==null"); 538 return; 539 } 540 logger.debug("refresh"); 541 Object view = session.getCurrentRequest().getView(); 542 if (view == null) { 543 showError("Display error","Nothing to refresh yet"); 544 logger.debug("Nothing to refresh yet"); 545 } else { 546 try { 547 loggerHtml.debug("genHTML on "+view+"("+Strings.hex(getResponse())+")"); 548 ((HTMLViewer)view).genHTML(getResponse().getWriter()); 549 } catch(Exception e) { 550 logger.error("refresh failed",e); 551 } finally { 552 getRequest().setResponse(); 553 } 554 } 555 } 556 557 static HttpServer httpServer; 558 public static HttpServer getHttpServer() { 559 if (httpServer==null) { 560 httpServer = new HttpServer(); 561 } 562 return httpServer; 563 } 564 565 /** Alreeady started GUIs */ 566 static HashSet startedGuis = new HashSet(); 567 /** TCP Ports the servlet engine listens to */ 568 static HashSet openedPorts = new HashSet(); 569 570 /** 571 * Start JAC's internal webserver. You can then interact with the 572 * application through an URL like this: 573 * http://<hostname>:<port>/<application>/<guiID> 574 * 575 * @param application name of the application 576 * @param guiIDs name of windows. gui_name[:port] 577 * @param defaultPort default TCP port on which to listen */ 578 public static void startWebServer(String application, String[] guiIDs, int defaultPort) 579 throws IOException, MultiException 580 { 581 loggerWeb.debug("startWebServer on port "+defaultPort); 582 httpServer = getHttpServer(); 583 HttpContext context = httpServer.getContext("/jac"); 584 585 ServletHandler servletHandler = new ServletHandler(); 586 for (int i=0; i<guiIDs.length;i++) { 587 if (startedGuis.contains(guiIDs[i])) { 588 loggerWeb.debug("skipping already registered GUI "+guiIDs[i]); 589 } else { 590 loggerWeb.debug("register GUI "+guiIDs[i]); 591 592 String split[] = Strings.split(guiIDs[i],":"); 593 String gui = split[0]; 594 int port = split.length>=2 ? Integer.parseInt(split[1]) : defaultPort; 595 596 if (!openedPorts.contains(new Integer(port))) { 597 httpServer.addListener(new InetAddrPort(port)); 598 openedPorts.add(new Integer(port)); 599 } 600 601 // Make sure the servlet will have the JAC ClassLoader 602 // or it won't get the right NameRepository 603 context.setClassLoader(WebDisplay.class.getClassLoader()); 604 605 servletHandler.addServlet("Jac","/"+gui, 606 "org.objectweb.jac.aspects.gui.web.JacLocalServlet"); 607 } 608 } 609 context.addHandler(servletHandler); 610 611 context = httpServer.getContext("/jac/resources"); 612 context.setBaseResource(new ClasspathResource()); 613 context.setClassLoader(WebDisplay.class.getClassLoader()); 614 context.addHandler(new ResourceHandler()); 615 616 startWebServer(); 617 } 618 619 /** 620 * Start the web server if it is not already started 621 */ 622 public static synchronized void startWebServer() throws MultiException { 623 if (httpServer.isStarted()) { 624 try { 625 httpServer.stop(); 626 } catch (Exception e) { 627 e.printStackTrace(); 628 } 629 } 630 httpServer.start(); 631 } 632 633 // View -> (String)id 634 Hashtable ids = new Hashtable(); 635 // (String)id -> View 636 Hashtable views = new Hashtable(); 637 638 /** 639 * Compute an id for a view and register it. 640 * @param view the view to register 641 * @return the id of the view 642 * @see #unregisterView(View) 643 * @see #getView(String) 644 */ 645 public String registerView(View view) { 646 String id = (String)ids.get(view); 647 if (id==null) { 648 id = Integer.toString(view.hashCode()); 649 ids.put(view,id); 650 views.put(id,view); 651 loggerViews.debug(toString()+".registerView("+view+") -> "+id); 652 } 653 return id; 654 } 655 656 /** 657 * Unregister a view. 658 * @param view the view to register 659 * @see #registerView(View) 660 */ 661 public void unregisterView(View view) { 662 Object id = ids.get(view); 663 loggerViews.debug(toString()+".unregisterView("+view+") "+id); 664 ids.remove(view); 665 if (id!=null) { 666 views.remove(id); 667 } 668 } 669 670 /** 671 * Returns the view registered with a given id 672 * @param id the id of the view 673 * @return the registered view with that id, null if there is none 674 * @see #registerView(View) 675 */ 676 public View getView(String id) { 677 loggerViews.debug(toString()+".getView("+id+")"); 678 return (View)views.get(id); 679 } 680 681 /** 682 * Gets the ID of a registered view. 683 * 684 * @param view the view 685 * @return the ID of the view 686 */ 687 public String getViewID(View view) { 688 return (String)ids.get(view); 689 } 690 691 // Strings 692 HashSet timedoutDialogs = new HashSet(); 693 public void addTimedoutDialog(DialogView dialog) { 694 timedoutDialogs.add(getViewID(dialog)); 695 } 696 /** 697 * Tells wether a view ID corresponds to a timedout dialog 698 */ 699 public boolean isTimedout(String viewID) { 700 return timedoutDialogs.contains(viewID); 701 } 702 703 String servletName; 704 /** 705 * Set the name of the servlet 706 * @param name the name of the servlet 707 */ 708 public void setServletName(String name) { 709 servletName = name; 710 } 711 /** 712 * Returns the name of the servlet 713 */ 714 public String getServletName() { 715 return servletName; 716 } 717 718 public void closeWindow(View window, boolean validate) { 719 loggerWeb.debug("closeWindow "+window); 720 Iterator i = session.getRequests().iterator(); 721 Request request = null; 722 while (i.hasNext()) { 723 request = (Request)i.next(); 724 if (request.getView().equals(window)) { 725 // we MUST do the close() before remove() because if 726 // close() fails with an exception, we do not want to 727 // remove. 728 ((View)request.getView()).close(validate); 729 session.getRequests().remove(request); 730 break; 731 } 732 } 733 } 734 735 public Session getSession() { 736 return session; 737 } 738 739 public boolean fillParameters(AbstractMethodItem method, Object[] parameters) { 740 Class[] paramTypes = method.getParameterTypes(); 741 for (int i=0; i<paramTypes.length; i++) { 742 if (Writer.class.isAssignableFrom(paramTypes[i]) 743 && parameters[i]==null) 744 { 745 String type = (String)method.getAttribute(GuiAC.MIME_TYPE); 746 if (type!=null) { 747 logger.info("Setting mime-type: "+type); 748 getResponse().setContentType(type+"; "+GuiAC.getEncoding()); 749 } 750 try { 751 parameters[i] = 752 new OutputStreamWriter( 753 getResponse().getOutputStream(), 754 GuiAC.getEncoding()); 755 grabResponse(); // So that no one else can used it 756 } catch(Exception e) { 757 logger.error("Failed to set Writer parameter "+i+ 758 " for "+method.getLongName(),e); 759 } 760 return paramTypes.length<=1; 761 } else if (OutputStream.class.isAssignableFrom(paramTypes[i]) 762 && parameters[i]==null) { 763 String type = (String)method.getAttribute(GuiAC.MIME_TYPE); 764 if (type!=null) { 765 logger.info("Setting mime-type: "+type); 766 getResponse().setContentType(type+"; "+GuiAC.getEncoding()); 767 } 768 try { 769 parameters[i] = getResponse().getOutputStream(); 770 grabResponse(); // So that no one else can used it 771 } catch(Exception e) { 772 logger.error("Failed to set OutputStream parameter "+i+ 773 " for "+method.getLongName(),e); 774 } 775 return paramTypes.length<=1; 776 } 777 } 778 779 return false; 780 } 781 782 /** 783 * Ensure that we will send any HTML output to the currently connection. 784 */ 785 public void onInvocationReturn(Object substance, AbstractMethodItem method) { 786 Class[] paramTypes = method.getParameterTypes(); 787 for (int i=0; i<paramTypes.length; i++) { 788 // TODO: only call setResponse() for the invocation for 789 // which the HttpResponse was grabbed 790 if (Writer.class.isAssignableFrom(paramTypes[i]) || 791 OutputStream.class.isAssignableFrom(paramTypes[i])) 792 { 793 getRequest().setResponse(); 794 } 795 } 796 } 797 798 }