Changeset 5242

Show
Ignore:
Timestamp:
08/22/08 07:25:23 (3 months ago)
Author:
artur.signell@…
Message:

Fixes for #2002 and #1642 - improved exception handling in CommunicationManager?

Location:
trunk/src/com/itmill/toolkit
Files:
2 added
3 modified

Legend:

Unmodified
Added
Removed
  • trunk/src/com/itmill/toolkit/Application.java

    r4746 r5242  
    66 
    77import java.net.MalformedURLException; 
     8import java.net.SocketException; 
    89import java.net.URL; 
    910import java.util.Collection; 
     
    2829import com.itmill.toolkit.terminal.URIHandler; 
    2930import com.itmill.toolkit.terminal.VariableOwner; 
     31import com.itmill.toolkit.terminal.gwt.server.ChangeVariablesErrorEvent; 
    3032import com.itmill.toolkit.ui.AbstractComponent; 
    3133import com.itmill.toolkit.ui.Component; 
     
    179181 
    180182    /** 
     183     * Application wide error handler which is used by default if an error is 
     184     * left unhandled. 
     185     */ 
     186    private Terminal.ErrorListener errorHandler = this; 
     187 
     188    /** 
    181189     * <p> 
    182190     * Gets a window by name. Returns <code>null</code> if the application is 
     
    10751083     */ 
    10761084    public void terminalError(Terminal.ErrorEvent event) { 
    1077         // throw it to standard error stream too 
    1078         event.getThrowable().printStackTrace(); 
     1085        Throwable t = event.getThrowable(); 
     1086        if (t instanceof SocketException) { 
     1087            // Most likely client browser closed socket 
     1088            System.err 
     1089                    .println("Warning: SocketException in CommunicationManager." 
     1090                            + " Most likely client (browser) closed socket."); 
     1091 
     1092        } else { 
     1093            // throw it to standard error stream too 
     1094            t.printStackTrace(); 
     1095        } 
    10791096 
    10801097        // Finds the original source of the error/exception 
     
    10861103        } else if (event instanceof ParameterHandler.ErrorEvent) { 
    10871104            owner = ((ParameterHandler.ErrorEvent) event).getParameterHandler(); 
     1105        } else if (event instanceof ChangeVariablesErrorEvent) { 
     1106            owner = ((ChangeVariablesErrorEvent) event).getComponent(); 
    10881107        } 
    10891108 
     
    11441163    public String getVersion() { 
    11451164        return "NONVERSIONED"; 
     1165    } 
     1166 
     1167    /** 
     1168     * Gets the application error handler. 
     1169     *  
     1170     * The default error handler is the application itself. 
     1171     *  
     1172     * @return Application error handler 
     1173     */ 
     1174    public Terminal.ErrorListener getErrorHandler() { 
     1175        return errorHandler; 
     1176    } 
     1177 
     1178    /** 
     1179     * Sets the application error handler. 
     1180     *  
     1181     * The default error handler is the application itself. 
     1182     *  
     1183     * @param errorHandler 
     1184     */ 
     1185    public void setErrorHandler(Terminal.ErrorListener errorHandler) { 
     1186        this.errorHandler = errorHandler; 
    11461187    } 
    11471188 
  • trunk/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java

    r4916 r5242  
    1313import java.io.PrintWriter; 
    1414import java.lang.reflect.Method; 
    15 import java.net.SocketException; 
    1615import java.text.DateFormatSymbols; 
    1716import java.text.SimpleDateFormat; 
     
    4948import com.itmill.toolkit.terminal.VariableOwner; 
    5049import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent; 
     50import com.itmill.toolkit.terminal.Terminal.ErrorEvent; 
     51import com.itmill.toolkit.ui.AbstractField; 
    5152import com.itmill.toolkit.ui.Component; 
    5253import com.itmill.toolkit.ui.Upload; 
     
    193194     * @param response 
    194195     * @throws IOException 
     196     * @throws ServletException 
    195197     */ 
    196198    public void handleUidlRequest(HttpServletRequest request, 
    197199            HttpServletResponse response, ApplicationServlet applicationServlet) 
    198             throws IOException { 
     200            throws IOException, ServletException { 
    199201 
    200202        // repaint requested or session has timed out and new one is created 
     
    206208                new OutputStreamWriter(out, "UTF-8"))); 
    207209 
    208         try { 
    209  
    210             // The rest of the process is synchronized with the application 
    211             // in order to guarantee that no parallel variable handling is 
    212             // made 
    213             synchronized (application) { 
    214  
    215                 // Finds the window within the application 
    216                 Window window = null; 
    217                 if (application.isRunning()) { 
    218                     window = getApplicationWindow(request, application); 
    219                     // Returns if no window found 
    220                     if (window == null) { 
    221                         // This should not happen, no windows exists but 
    222                         // application is still open. 
    223                         System.err 
    224                                 .println("Warning, could not get window for application with request URI " 
    225                                         + request.getRequestURI()); 
     210        // The rest of the process is synchronized with the application 
     211        // in order to guarantee that no parallel variable handling is 
     212        // made 
     213        synchronized (application) { 
     214 
     215            // Finds the window within the application 
     216            Window window = null; 
     217            if (application.isRunning()) { 
     218                window = getApplicationWindow(request, application); 
     219                // Returns if no window found 
     220                if (window == null) { 
     221                    // This should not happen, no windows exists but 
     222                    // application is still open. 
     223                    System.err 
     224                            .println("Warning, could not get window for application with request URI " 
     225                                    + request.getRequestURI()); 
     226                    return; 
     227                } 
     228            } else { 
     229                // application has been closed 
     230                endApplication(request, response, application); 
     231                return; 
     232            } 
     233 
     234            // Change all variables based on request parameters 
     235            if (!handleVariables(request, application)) { 
     236                // var inconsistency; the client is probably out-of-sync 
     237                SystemMessages ci = null; 
     238                try { 
     239                    Method m = application.getClass().getMethod( 
     240                            "getSystemMessages", null); 
     241                    ci = (Application.SystemMessages) m.invoke(null, null); 
     242                } catch (Exception e2) { 
     243                    // Not critical, but something is still wrong; print 
     244                    // stacktrace 
     245                    e2.printStackTrace(); 
     246                } 
     247                if (ci != null) { 
     248                    String msg = ci.getOutOfSyncMessage(); 
     249                    String cap = ci.getOutOfSyncCaption(); 
     250                    if (msg != null || cap != null) { 
     251                        applicationServlet.criticalNotification(request, 
     252                                response, cap, msg, ci.getOutOfSyncURL()); 
     253                        // will reload page after this 
    226254                        return; 
    227255                    } 
     256                } 
     257                // No message to show, let's just repaint all. 
     258                System.err 
     259                        .println("Warning: variable inconsistency - client is probably out-of-sync, repainting all."); 
     260                repaintAll = true; 
     261 
     262            } 
     263 
     264            // If repaint is requested, clean all ids in this root window 
     265            if (repaintAll) { 
     266                for (final Iterator it = idPaintableMap.keySet().iterator(); it 
     267                        .hasNext();) { 
     268                    final Component c = (Component) idPaintableMap.get(it 
     269                            .next()); 
     270                    if (isChildOf(window, c)) { 
     271                        it.remove(); 
     272                        paintableIdMap.remove(c); 
     273                    } 
     274                } 
     275            } 
     276 
     277            // Removes application if it has stopped during variable changes 
     278            if (!application.isRunning()) { 
     279                endApplication(request, response, application); 
     280                return; 
     281            } 
     282 
     283            // Sets the response type 
     284            response.setContentType("application/json; charset=UTF-8"); 
     285            // some dirt to prevent cross site scripting 
     286            outWriter.print("for(;;);[{"); 
     287 
     288            outWriter.print("\"changes\":["); 
     289 
     290            // re-get mainwindow - may have been changed 
     291            Window newWindow = getApplicationWindow(request, application); 
     292            if (newWindow != window) { 
     293                window = newWindow; 
     294                repaintAll = true; 
     295            } 
     296 
     297            JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter, 
     298                    !repaintAll); 
     299 
     300            // Paints components 
     301            ArrayList paintables; 
     302            if (repaintAll) { 
     303                paintables = new ArrayList(); 
     304                paintables.add(window); 
     305 
     306                // Reset sent locales 
     307                locales = null; 
     308                requireLocale(application.getLocale().toString()); 
     309 
     310            } else { 
     311                // remove detached components from paintableIdMap so they 
     312                // can be GC'ed 
     313                for (Iterator it = paintableIdMap.keySet().iterator(); it 
     314                        .hasNext();) { 
     315                    Component p = (Component) it.next(); 
     316                    if (p.getApplication() == null) { 
     317                        idPaintableMap.remove(paintableIdMap.get(p)); 
     318                        it.remove(); 
     319                        dirtyPaintabletSet.remove(p); 
     320                        p.removeListener(this); 
     321                    } 
     322                } 
     323                paintables = getDirtyComponents(window); 
     324            } 
     325            if (paintables != null) { 
     326 
     327                // We need to avoid painting children before parent. 
     328                // This is ensured by ordering list by depth in component 
     329                // tree 
     330                Collections.sort(paintables, new Comparator() { 
     331                    public int compare(Object o1, Object o2) { 
     332                        Component c1 = (Component) o1; 
     333                        Component c2 = (Component) o2; 
     334                        int d1 = 0; 
     335                        while (c1.getParent() != null) { 
     336                            d1++; 
     337                            c1 = c1.getParent(); 
     338                        } 
     339                        int d2 = 0; 
     340                        while (c2.getParent() != null) { 
     341                            d2++; 
     342                            c2 = c2.getParent(); 
     343                        } 
     344                        if (d1 < d2) { 
     345                            return -1; 
     346                        } 
     347                        if (d1 > d2) { 
     348                            return 1; 
     349                        } 
     350                        return 0; 
     351                    } 
     352                }); 
     353 
     354                for (final Iterator i = paintables.iterator(); i.hasNext();) { 
     355                    final Paintable p = (Paintable) i.next(); 
     356 
     357                    // TODO CLEAN 
     358                    if (p instanceof Window) { 
     359                        final Window w = (Window) p; 
     360                        if (w.getTerminal() == null) { 
     361                            w.setTerminal(application.getMainWindow() 
     362                                    .getTerminal()); 
     363                        } 
     364                    } 
     365                    /* 
     366                     * This does not seem to happen in tk5, but remember this 
     367                     * case: else if (p instanceof Component) { if (((Component) 
     368                     * p).getParent() == null || ((Component) 
     369                     * p).getApplication() == null) { // Component requested 
     370                     * repaint, but is no // longer attached: skip 
     371                     * paintablePainted(p); continue; } } 
     372                     */ 
     373 
     374                    // TODO we may still get changes that have been 
     375                    // rendered already (changes with only cached flag) 
     376                    if (paintTarget.needsToBePainted(p)) { 
     377                        paintTarget.startTag("change"); 
     378                        paintTarget.addAttribute("format", "uidl"); 
     379                        final String pid = getPaintableId(p); 
     380                        paintTarget.addAttribute("pid", pid); 
     381 
     382                        p.paint(paintTarget); 
     383 
     384                        paintTarget.endTag("change"); 
     385                    } 
     386                    paintablePainted(p); 
     387                } 
     388            } 
     389 
     390            paintTarget.close(); 
     391            outWriter.print("]"); // close changes 
     392 
     393            outWriter.print(", \"meta\" : {"); 
     394            boolean metaOpen = false; 
     395 
     396            if (repaintAll) { 
     397                metaOpen = true; 
     398                outWriter.write("\"repaintAll\":true"); 
     399            } 
     400 
     401            // add meta instruction for client to set focus if it is set 
     402            final Paintable f = (Paintable) application.consumeFocus(); 
     403            if (f != null) { 
     404                if (metaOpen) { 
     405                    outWriter.write(","); 
     406                } 
     407                outWriter.write("\"focus\":\"" + getPaintableId(f) + "\""); 
     408            } 
     409 
     410            outWriter.print("}, \"resources\" : {"); 
     411 
     412            // Precache custom layouts 
     413            String themeName = window.getTheme(); 
     414            if (request.getParameter("theme") != null) { 
     415                themeName = request.getParameter("theme"); 
     416            } 
     417            if (themeName == null) { 
     418                themeName = "default"; 
     419            } 
     420 
     421            // TODO We should only precache the layouts that are not 
     422            // cached already 
     423            int resourceIndex = 0; 
     424            for (final Iterator i = paintTarget.getPreCachedResources() 
     425                    .iterator(); i.hasNext();) { 
     426                final String resource = (String) i.next(); 
     427                InputStream is = null; 
     428                try { 
     429                    is = applicationServlet 
     430                            .getServletContext() 
     431                            .getResourceAsStream( 
     432                                    "/" 
     433                                            + ApplicationServlet.THEME_DIRECTORY_PATH 
     434                                            + themeName + "/" + resource); 
     435                } catch (final Exception e) { 
     436                    e.printStackTrace(); 
     437                } 
     438                if (is != null) { 
     439 
     440                    outWriter.print((resourceIndex++ > 0 ? ", " : "") + "\"" 
     441                            + resource + "\" : "); 
     442                    final StringBuffer layout = new StringBuffer(); 
     443 
     444                    try { 
     445                        final InputStreamReader r = new InputStreamReader(is); 
     446                        final char[] buffer = new char[20000]; 
     447                        int charsRead = 0; 
     448                        while ((charsRead = r.read(buffer)) > 0) { 
     449                            layout.append(buffer, 0, charsRead); 
     450                        } 
     451                        r.close(); 
     452                    } catch (final java.io.IOException e) { 
     453                        System.err.println("Resource transfer failed:  " 
     454                                + request.getRequestURI() + ". (" 
     455                                + e.getMessage() + ")"); 
     456                    } 
     457                    outWriter.print("\"" 
     458                            + JsonPaintTarget.escapeJSON(layout.toString()) 
     459                            + "\""); 
    228460                } else { 
    229                     // application has been closed 
    230                     endApplication(request, response, application); 
    231                     return; 
    232                 } 
    233  
    234                 // Change all variables based on request parameters 
    235                 if (!handleVariables(request, application)) { 
    236                     // var inconsistency; the client is probably out-of-sync 
    237                     SystemMessages ci = null; 
    238                     try { 
    239                         Method m = application.getClass().getMethod( 
    240                                 "getSystemMessages", null); 
    241                         ci = (Application.SystemMessages) m.invoke(null, null); 
    242                     } catch (Exception e2) { 
    243                         // Not critical, but something is still wrong; print 
    244                         // stacktrace 
    245                         e2.printStackTrace(); 
    246                     } 
    247                     if (ci != null) { 
    248                         String msg = ci.getOutOfSyncMessage(); 
    249                         String cap = ci.getOutOfSyncCaption(); 
    250                         if (msg != null || cap != null) { 
    251                             applicationServlet.criticalNotification(request, 
    252                                     response, cap, msg, ci.getOutOfSyncURL()); 
    253                             // will reload page after this 
    254                             return; 
    255                         } 
    256                     } 
    257                     // No message to show, let's just repaint all. 
    258                     System.err 
    259                             .println("Warning: variable inconsistency - client is probably out-of-sync, repainting all."); 
    260                     repaintAll = true; 
    261  
    262                 } 
    263  
    264                 // If repaint is requested, clean all ids in this root window 
    265                 if (repaintAll) { 
    266                     for (final Iterator it = idPaintableMap.keySet().iterator(); it 
    267                             .hasNext();) { 
    268                         final Component c = (Component) idPaintableMap.get(it 
    269                                 .next()); 
    270                         if (isChildOf(window, c)) { 
    271                             it.remove(); 
    272                             paintableIdMap.remove(c); 
    273                         } 
    274                     } 
    275                 } 
    276  
    277                 // Removes application if it has stopped during variable changes 
    278                 if (!application.isRunning()) { 
    279                     endApplication(request, response, application); 
    280                     return; 
    281                 } 
    282  
    283                 // Sets the response type 
    284                 response.setContentType("application/json; charset=UTF-8"); 
    285                 // some dirt to prevent cross site scripting 
    286                 outWriter.print("for(;;);[{"); 
    287  
    288                 outWriter.print("\"changes\":["); 
    289  
    290                 // re-get mainwindow - may have been changed 
    291                 Window newWindow = getApplicationWindow(request, application); 
    292                 if (newWindow != window) { 
    293                     window = newWindow; 
    294                     repaintAll = true; 
    295                 } 
    296  
    297                 JsonPaintTarget paintTarget = new JsonPaintTarget(this, 
    298                         outWriter, !repaintAll); 
    299  
    300                 // Paints components 
    301                 ArrayList paintables; 
    302                 if (repaintAll) { 
    303                     paintables = new ArrayList(); 
    304                     paintables.add(window); 
    305  
    306                     // Reset sent locales 
    307                     locales = null; 
    308                     requireLocale(application.getLocale().toString()); 
    309  
    310                 } else { 
    311                     // remove detached components from paintableIdMap so they 
    312                     // can be GC'ed 
    313                     for (Iterator it = paintableIdMap.keySet().iterator(); it 
    314                             .hasNext();) { 
    315                         Component p = (Component) it.next(); 
    316                         if (p.getApplication() == null) { 
    317                             idPaintableMap.remove(paintableIdMap.get(p)); 
    318                             it.remove(); 
    319                             dirtyPaintabletSet.remove(p); 
    320                             p.removeListener(this); 
    321                         } 
    322                     } 
    323                     paintables = getDirtyComponents(window); 
    324                 } 
    325                 if (paintables != null) { 
    326  
    327                     // We need to avoid painting children before parent. 
    328                     // This is ensured by ordering list by depth in component 
    329                     // tree 
    330                     Collections.sort(paintables, new Comparator() { 
    331                         public int compare(Object o1, Object o2) { 
    332                             Component c1 = (Component) o1; 
    333                             Component c2 = (Component) o2; 
    334                             int d1 = 0; 
    335                             while (c1.getParent() != null) { 
    336                                 d1++; 
    337                                 c1 = c1.getParent(); 
    338                             } 
    339                             int d2 = 0; 
    340                             while (c2.getParent() != null) { 
    341                                 d2++; 
    342                                 c2 = c2.getParent(); 
    343                             } 
    344                             if (d1 < d2) { 
    345                                 return -1; 
    346                             } 
    347                             if (d1 > d2) { 
    348                                 return 1; 
    349                             } 
    350                             return 0; 
    351                         } 
    352                     }); 
    353  
    354                     for (final Iterator i = paintables.iterator(); i.hasNext();) { 
    355                         final Paintable p = (Paintable) i.next(); 
    356  
    357                         // TODO CLEAN 
    358                         if (p instanceof Window) { 
    359                             final Window w = (Window) p; 
    360                             if (w.getTerminal() == null) { 
    361                                 w.setTerminal(application.getMainWindow() 
    362                                         .getTerminal()); 
    363                             } 
    364                         } 
    365                         /* 
    366                          * This does not seem to happen in tk5, but remember 
    367                          * this case: else if (p instanceof Component) { if 
    368                          * (((Component) p).getParent() == null || ((Component) 
    369                          * p).getApplication() == null) { // Component requested 
    370                          * repaint, but is no // longer attached: skip 
    371                          * paintablePainted(p); continue; } } 
    372                          */ 
    373  
    374                         // TODO we may still get changes that have been 
    375                         // rendered already (changes with only cached flag) 
    376                         if (paintTarget.needsToBePainted(p)) { 
    377                             paintTarget.startTag("change"); 
    378                             paintTarget.addAttribute("format", "uidl"); 
    379                             final String pid = getPaintableId(p); 
    380                             paintTarget.addAttribute("pid", pid); 
    381  
    382                             p.paint(paintTarget); 
    383  
    384                             paintTarget.endTag("change"); 
    385                         } 
    386                         paintablePainted(p); 
    387                     } 
    388                 } 
    389  
    390                 paintTarget.close(); 
    391                 outWriter.print("]"); // close changes 
    392  
    393                 outWriter.print(", \"meta\" : {"); 
    394                 boolean metaOpen = false; 
    395  
    396                 if (repaintAll) { 
    397                     metaOpen = true; 
    398                     outWriter.write("\"repaintAll\":true"); 
    399                 } 
    400  
    401                 // add meta instruction for client to set focus if it is set 
    402                 final Paintable f = (Paintable) application.consumeFocus(); 
    403                 if (f != null) { 
    404                     if (metaOpen) { 
    405                         outWriter.write(","); 
    406                     } 
    407                     outWriter.write("\"focus\":\"" + getPaintableId(f) + "\""); 
    408                 } 
    409  
    410                 outWriter.print("}, \"resources\" : {"); 
    411  
    412                 // Precache custom layouts 
    413                 String themeName = window.getTheme(); 
    414                 if (request.getParameter("theme") != null) { 
    415                     themeName = request.getParameter("theme"); 
    416                 } 
    417                 if (themeName == null) { 
    418                     themeName = "default"; 
    419                 } 
    420  
    421                 // TODO We should only precache the layouts that are not 
    422                 // cached already 
    423                 int resourceIndex = 0; 
    424                 for (final Iterator i = paintTarget.getPreCachedResources() 
    425                         .iterator(); i.hasNext();) { 
    426                     final String resource = (String) i.next(); 
    427                     InputStream is = null; 
    428                     try { 
    429                         is = applicationServlet 
    430                                 .getServletContext() 
    431                                 .getResourceAsStream( 
    432                                         "/" 
    433                                                 + ApplicationServlet.THEME_DIRECTORY_PATH 
    434                                                 + themeName + "/" + resource); 
    435                     } catch (final Exception e) { 
    436                         e.printStackTrace(); 
    437                     } 
    438                     if (is != null) { 
    439  
    440                         outWriter.print((resourceIndex++ > 0 ? ", " : "") 
    441                                 + "\"" + resource + "\" : "); 
    442                         final StringBuffer layout = new StringBuffer(); 
    443  
    444                         try { 
    445                             final InputStreamReader r = new InputStreamReader( 
    446                                     is); 
    447                             final char[] buffer = new char[20000]; 
    448                             int charsRead = 0; 
    449                             while ((charsRead = r.read(buffer)) > 0) { 
    450                                 layout.append(buffer, 0, charsRead); 
    451                             } 
    452                             r.close(); 
    453                         } catch (final java.io.IOException e) { 
    454                             System.err.println("Resource transfer failed:  " 
    455