use of org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError in project scout.rt by eclipse.
the class FutureAwaitTest method testAwaitDoneAndGetWithTimeout_Interrupted.
@Test(timeout = 5000)
public void testAwaitDoneAndGetWithTimeout_Interrupted() throws java.lang.InterruptedException {
final BlockingCountDownLatch setupLatch = new BlockingCountDownLatch(1);
// Init
final IFuture<String> future = Jobs.schedule(new Callable<String>() {
@Override
public String call() throws Exception {
setupLatch.countDownAndBlock();
return "result";
}
}, Jobs.newInput().withRunContext(RunContexts.copyCurrent()));
// Run the test in a separate thread
IFuture<Void> controller = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
Thread.currentThread().interrupt();
try {
future.awaitDoneAndGet(10, TimeUnit.SECONDS);
fail("interruption expected");
} catch (ThreadInterruptedError e) {
assertTrue(Thread.currentThread().isInterrupted());
}
}
}, Jobs.newInput());
controller.awaitDoneAndGet(10, TimeUnit.SECONDS);
setupLatch.unblock();
future.awaitDone(10, TimeUnit.SECONDS);
}
use of org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError in project scout.rt by eclipse.
the class SessionStore method doHousekeeping.
/**
* Checks if the client session is still used by a UI session. If not, it is stopped and removed form the store.
*/
protected void doHousekeeping(final IClientSession clientSession) {
m_writeLock.lock();
try {
if (IFuture.CURRENT.get().isCancelled()) {
return;
}
m_housekeepingFutures.remove(clientSession.getId());
if (!clientSession.isActive() || clientSession.isStopping()) {
LOG.info("Session housekeeping: Client session {} is {}, removing it from store", clientSession.getId(), (!clientSession.isActive() ? "inactive" : "stopping"));
removeClientSession(clientSession);
return;
}
// Check if the client session is referenced by any UI session
Set<IUiSession> uiSessions = m_uiSessionsByClientSession.get(clientSession);
LOG.debug("Session housekeeping: Client session {} referenced by {} UI sessions", clientSession.getId(), (uiSessions == null ? 0 : uiSessions.size()));
if (uiSessions == null || uiSessions.isEmpty()) {
LOG.info("Session housekeeping: Shutting down client session with ID {} because it is not used anymore", clientSession.getId());
try {
final IFuture<Void> future = ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
forceClientSessionShutdown(clientSession);
}
}, ModelJobs.newInput(ClientRunContexts.empty().withSession(clientSession, true)).withName("Force shutting down client session {} by session housekeeping", clientSession.getId()));
int timeout = CONFIG.getPropertyValue(SessionStoreHousekeepingMaxWaitShutdownProperty.class).intValue();
try {
// NOSONAR
future.awaitDone(timeout, TimeUnit.SECONDS);
} catch (TimedOutError e) {
// NOSONAR
LOG.warn("Client session did no stop within {} seconds. Canceling shutdown job.", timeout);
future.cancel(true);
}
} catch (ThreadInterruptedError e) {
LOG.warn("Interruption encountered while waiting for client session {} to stop. Continuing anyway.", clientSession.getId(), e);
} finally {
removeClientSession(clientSession);
}
}
} finally {
m_writeLock.unlock();
}
}
use of org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError in project scout.rt by eclipse.
the class SessionStore method valueUnbound.
@Override
public void valueUnbound(final HttpSessionBindingEvent event) {
if (!m_httpSessionValid) {
// valueUnbound() has already been executed
return;
}
m_httpSessionValid = false;
LOG.info("Detected invalidation of HTTP session {}, cleaning up {} client sessions and {} UI sessions", m_httpSessionId, m_clientSessionMap.size(), m_uiSessionMap.size());
final List<IFuture<?>> futures = new ArrayList<>();
// Stop all client sessions (in parallel model jobs)
try {
int timeout = CONFIG.getPropertyValue(SessionStoreMaxWaitWriteLockProperty.class).intValue();
if (m_writeLock.tryLock(timeout, TimeUnit.SECONDS)) {
try {
for (final IClientSession clientSession : m_clientSessionMap.values()) {
futures.add(ModelJobs.schedule(new IRunnable() {
@Override
public void run() {
LOG.debug("Shutting down client session with ID {} due to invalidation of HTTP session", clientSession.getId());
forceClientSessionShutdown(clientSession);
removeClientSession(clientSession);
}
}, ModelJobs.newInput(ClientRunContexts.empty().withSession(clientSession, true)).withName("Closing desktop due to HTTP session invalidation")));
}
} finally {
m_writeLock.unlock();
}
} else {
LOG.warn("Could not acquire write lock within {} seconds: [HTTP session: {}, uiSessionMap: {}, clientSessionMap: {}, uiSessionsByClientSession: {}]", timeout, m_uiSessionMap.size(), m_clientSessionMap.size(), m_uiSessionsByClientSession.size());
}
} catch (InterruptedException e) {
LOG.warn("Interrupted while waiting on session store write lock", e);
}
if (futures.isEmpty()) {
return;
}
LOG.debug("Waiting for {} client sessions to stop...", futures.size());
try {
// Wait for all client sessions to stop. This is done in sync to ensure the session is not invalidated before the attached scout session has been stopped.
// Otherwise we would have a running scout session on an invalidated http session.
// Furthermore: on webapp shutdown we must first stop all sessions (scout and http) before we stop the platform. Therefore the stop must be sync!
Jobs.getJobManager().awaitDone(Jobs.newFutureFilterBuilder().andMatchFuture(futures).toFilter(), CONFIG.getPropertyValue(SessionStoreMaxWaitAllShutdownProperty.class), TimeUnit.SECONDS);
LOG.info("Session shutdown complete.");
} catch (ThreadInterruptedError e) {
LOG.warn("Interruption encountered while waiting for all client session to stop. Continuing anyway.", e);
} catch (TimedOutError e) {
LOG.warn("Timeout encountered while waiting for all client session to stop. Canceling still running client session shutdown jobs.", e);
// timeout while waiting for the sessions to stop. Force cancel them.
Jobs.getJobManager().cancel(Jobs.newFutureFilterBuilder().andMatchFuture(futures).andMatchNotState(JobState.DONE).toFilter(), true);
}
// Check if everything was cleaned up correctly ("leak detection").
// Read map sizes outside a lock - dirty reads are acceptable here
final int uiSessionMapSize = m_uiSessionMap.size();
final int clientSessionMapSize = m_clientSessionMap.size();
final int uiSessionsByClientSessionSize = m_uiSessionsByClientSession.size();
if (uiSessionMapSize + clientSessionMapSize + uiSessionsByClientSessionSize > 0) {
LOG.warn("Leak detection - Session store not empty after HTTP session invalidation: [uiSessionMap: {}, clientSessionMap: {}, uiSessionsByClientSession: {}]", uiSessionMapSize, clientSessionMapSize, uiSessionsByClientSessionSize);
}
}
use of org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError in project scout.rt by eclipse.
the class UiSession method processFileUpload.
@Override
public JSONObject processFileUpload(HttpServletRequest req, HttpServletResponse res, final IBinaryResourceConsumer resourceConsumer, final List<BinaryResource> uploadResources, final Map<String, String> uploadProperties) {
final ClientRunContext clientRunContext = ClientRunContexts.copyCurrent().withSession(m_clientSession, true);
m_httpContext.set(req, res);
try {
m_processingJsonRequest = true;
try {
// 1. Process the JSON request.
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
resourceConsumer.consumeBinaryResource(uploadResources, uploadProperties);
}
}, createFileUploadModelJobInput(clientRunContext));
// 2. Wait for all model jobs of the session.
BEANS.get(UiJobs.class).awaitModelJobs(m_clientSession, ExceptionHandler.class);
} finally {
// Reset this flag _before_ the "response-to-json" job (#3), because writing to the response while transforming would be unsafe and unreliable.
m_processingJsonRequest = false;
}
// 3. Transform the response to JSON.
final IFuture<JSONObject> future = ModelJobs.schedule(newResponseToJsonTransformer(), ModelJobs.newInput(clientRunContext.copy().withRunMonitor(// separate RunMonitor to not cancel 'response-to-json' job once processing is cancelled
BEANS.get(RunMonitor.class))).withName("Transforming response to JSON").withExecutionHint(UiJobs.EXECUTION_HINT_RESPONSE_TO_JSON).withExceptionHandling(null, // Propagate exception to caller (UIServlet)
false));
try {
return BEANS.get(UiJobs.class).awaitAndGet(future);
} catch (ThreadInterruptedError e) {
// NOSONAR
future.cancel(true);
return null;
} catch (FutureCancelledError e) {
// NOSONAR
return null;
}
} finally {
m_httpContext.clear();
if (m_disposing) {
dispose();
}
}
}
use of org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError in project scout.rt by eclipse.
the class UiSession method processJsonRequest.
@Override
public JSONObject processJsonRequest(final HttpServletRequest servletRequest, final HttpServletResponse servletResponse, final JsonRequest jsonRequest) {
if (isAlreadyProcessed(jsonRequest)) {
JSONObject response = m_responseHistory.getResponseForRequest(jsonRequest.getSequenceNo());
LOG.debug("Request #{} was already processed. Sending back response from history.", jsonRequest.getSequenceNo());
return response;
}
final ClientRunContext clientRunContext = ClientRunContexts.copyCurrent().withSession(m_clientSession, true);
m_httpContext.set(servletRequest, servletResponse);
m_currentJsonRequest = jsonRequest;
try {
m_processingJsonRequest = true;
try {
// 1. Process the JSON request.
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
processJsonRequestInternal();
}
}, createJsonRequestModelJobInput(jsonRequest, clientRunContext));
// 2. Wait for all model jobs of the session.
BEANS.get(UiJobs.class).awaitModelJobs(m_clientSession, ExceptionHandler.class);
} finally {
// Reset this flag _before_ the "response-to-json" job (#3), because writing to the response while transforming would be unsafe and unreliable.
m_processingJsonRequest = false;
}
// 3. Transform the response to JSON.
final IFuture<JSONObject> future = ModelJobs.schedule(newResponseToJsonTransformer(), ModelJobs.newInput(clientRunContext.copy().withRunMonitor(// separate RunMonitor to not cancel 'response-to-json' job once processing is cancelled
BEANS.get(RunMonitor.class))).withName("Transforming response to JSON").withExecutionHint(UiJobs.EXECUTION_HINT_RESPONSE_TO_JSON).withExecutionHint(UiJobs.EXECUTION_HINT_POLL_REQUEST, jsonRequest.getRequestType() == RequestType.POLL_REQUEST).withExceptionHandling(null, // Propagate exception to caller (UIServlet)
false));
try {
return BEANS.get(UiJobs.class).awaitAndGet(future);
} catch (ThreadInterruptedError e) {
// NOSONAR
future.cancel(true);
return null;
} catch (FutureCancelledError e) {
// NOSONAR
return null;
}
} finally {
setRequestProcessed(jsonRequest);
m_httpContext.clear();
m_currentJsonRequest = null;
if (m_disposing) {
dispose();
}
if (LOG.isDebugEnabled()) {
LOG.debug("Adapter count after request: {}", m_jsonAdapterRegistry.size());
}
}
}
Aggregations