| 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 |
| | 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 | + "\""); |
| 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 | |   |