public FreeMarkerServiceResponse executeTemplate()

in src/main/java/org/apache/freemarker/onlinetester/services/FreeMarkerService.java [135:232]


	public FreeMarkerServiceResponse executeTemplate(ExecuteTemplateArgs args)
            throws RejectedExecutionException {
        Objects.requireNonNull(templateExecutor, "templateExecutor was null - was postConstruct ever called?");
        
        final ExecuteTempalteTask task = new ExecuteTempalteTask(args);
        Future<FreeMarkerServiceResponse> future = templateExecutor.submit(task);
        
        synchronized (task) {
            while (!task.isTemplateExecutionStarted() && !task.isTaskEnded() && !future.isDone()) {
                try {
                    task.wait(50); // Timeout is needed to periodically check future.isDone()
                } catch (InterruptedException e) {
                    throw new FreeMarkerServiceException("Template execution task was interrupted.", e);
                }
            }
        }
        
        try {
            return future.get(maxTemplateExecutionTime, TimeUnit.MILLISECONDS);
        } catch (ExecutionException e) {
            throw new FreeMarkerServiceException("Template execution task unexpectedly failed", e.getCause());
        } catch (InterruptedException e) {
            throw new FreeMarkerServiceException("Template execution task was interrupted.", e);
        } catch (TimeoutException e) {
            // Exactly one interruption should be enough, and it should abort template processing pretty much
            // immediately. But to be on the safe side we will interrupt in a loop, with a timeout.
            final long abortionLoopStartTime = System.currentTimeMillis();
            long timeLeft = ABORTION_LOOP_TIME_LIMIT;
            boolean templateExecutionEnded = false;
            do {
                synchronized (task) {
                    Thread templateExecutorThread = task.getTemplateExecutorThread();
                    if (templateExecutorThread == null) {
                        templateExecutionEnded = true;
                    } else {
                        logger.debug("Trying to interrupt overly long template processing ({} ms left).", timeLeft);
                        FreeMarkerInternalsAccessor.interruptTemplateProcessing(templateExecutorThread);
                    }
                } // sync
                if (!templateExecutionEnded) {
                    try {
                        timeLeft = ABORTION_LOOP_TIME_LIMIT - (System.currentTimeMillis() - abortionLoopStartTime);
                        if (timeLeft > 0) {
                            Thread.sleep(ABORTION_LOOP_INTERRUPTION_DISTANCE);
                        }
                    } catch (InterruptedException eInt) {
                        logger.error("Template execution abortion loop was interrupted", eInt);
                        timeLeft = 0;
                    }
                }
            } while (!templateExecutionEnded && timeLeft > 0);
            
    		// If a slow operation didn't react to Thread.interrupt, we better risk this than allow
    		// the depletion of the thread pool:
            if (!templateExecutionEnded) {
	            synchronized (task) {
	                Thread templateExecutorThread = task.getTemplateExecutorThread();
	                if (templateExecutorThread == null) {
	                    templateExecutionEnded = true;
	                } else {
	                    if (logger.isWarnEnabled()) {
	                    	logger.warn("Calling Thread.stop() on unresponsive long template processing, which didn't "
	                    			+ "respond to Template.interrupt() on time. Service state may will be inconsistent; "
	                    			+ "JVM restart recommended!\n"
	                    			+ "Template (quoted): " + StringUtil.jQuote(args.templateSourceCode));
	                    }
	                    templateExecutorThread.stop();
	                }
	            } // sync
                try {
                	// We should now receive a result from the task, so that we don't have to die with HTTP 500
					Thread.sleep(THREAD_STOP_EFFECT_WAIT_TIME);
		            synchronized (task) {
		                Thread templateExecutorThread = task.getTemplateExecutorThread();
		                if (templateExecutorThread == null) {
		                    templateExecutionEnded = true;
		                }
		            } // sync
				} catch (InterruptedException e2) {
					// Just continue...
				}
            }
            
            if (templateExecutionEnded) {
                logger.debug("Long template processing has ended.");
                try {
                    return future.get();
                } catch (InterruptedException | ExecutionException e2) {
                    throw new FreeMarkerServiceException("Failed to get result from template executor task", e2);
                }
            } else {
                throw new FreeMarkerServiceException(
                        "Couldn't stop long running template processing within " + ABORTION_LOOP_TIME_LIMIT
                        + " ms. It's possibly stuck forever. Such problems can exhaust the executor pool. "
                        + "Template (quoted): " + StringUtil.jQuote(args.templateSourceCode));
            }
        }
    }