001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.session.mgt; 020 021import java.util.concurrent.Executors; 022import java.util.concurrent.ScheduledExecutorService; 023import java.util.concurrent.ThreadFactory; 024import java.util.concurrent.TimeUnit; 025import java.util.concurrent.atomic.AtomicInteger; 026 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030 031/** 032 * SessionValidationScheduler implementation that uses a 033 * {@link ScheduledExecutorService} to call {@link ValidatingSessionManager#validateSessions()} every 034 * <em>{@link #getSessionValidationInterval sessionValidationInterval}</em> milliseconds. 035 * 036 * @since 0.9 037 */ 038public class ExecutorServiceSessionValidationScheduler implements SessionValidationScheduler, Runnable { 039 040 //TODO - complete JavaDoc 041 042 /** 043 * Private internal log instance. 044 */ 045 private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorServiceSessionValidationScheduler.class); 046 047 ValidatingSessionManager sessionManager; 048 private ScheduledExecutorService service; 049 private long sessionValidationInterval = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL; 050 private boolean enabled; 051 private String threadNamePrefix = "SessionValidationThread-"; 052 053 public ExecutorServiceSessionValidationScheduler() { 054 super(); 055 } 056 057 public ExecutorServiceSessionValidationScheduler(ValidatingSessionManager sessionManager) { 058 this.sessionManager = sessionManager; 059 } 060 061 public ValidatingSessionManager getSessionManager() { 062 return sessionManager; 063 } 064 065 public void setSessionManager(ValidatingSessionManager sessionManager) { 066 this.sessionManager = sessionManager; 067 } 068 069 public long getSessionValidationInterval() { 070 return sessionValidationInterval; 071 } 072 073 public void setSessionValidationInterval(long sessionValidationInterval) { 074 this.sessionValidationInterval = sessionValidationInterval; 075 } 076 077 public boolean isEnabled() { 078 return this.enabled; 079 } 080 081 public void setThreadNamePrefix(String threadNamePrefix) { 082 this.threadNamePrefix = threadNamePrefix; 083 } 084 085 public String getThreadNamePrefix() { 086 return this.threadNamePrefix; 087 } 088 089 /** 090 * Creates a single thread {@link ScheduledExecutorService} to validate sessions at fixed intervals 091 * and enables this scheduler. The executor is created as a daemon thread to allow JVM to shut down 092 */ 093 //TODO Implement an integration test to test for jvm exit as part of the standalone example 094 // (so we don't have to change the unit test execution model for the core module) 095 public void enableSessionValidation() { 096 if (this.sessionValidationInterval > 0L) { 097 this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { 098 private final AtomicInteger count = new AtomicInteger(1); 099 100 public Thread newThread(Runnable r) { 101 Thread thread = new Thread(r); 102 thread.setDaemon(true); 103 thread.setName(threadNamePrefix + count.getAndIncrement()); 104 return thread; 105 } 106 }); 107 this.service.scheduleAtFixedRate(this, sessionValidationInterval, 108 sessionValidationInterval, TimeUnit.MILLISECONDS); 109 } 110 this.enabled = true; 111 } 112 113 public void run() { 114 if (LOGGER.isDebugEnabled()) { 115 LOGGER.debug("Executing session validation..."); 116 } 117 Thread.currentThread().setUncaughtExceptionHandler((t, e) -> { 118 LOGGER.error("Error while validating the session, the thread will be stopped and session validation disabled", e); 119 this.disableSessionValidation(); 120 }); 121 long startTime = System.currentTimeMillis(); 122 try { 123 this.sessionManager.validateSessions(); 124 } catch (RuntimeException e) { 125 LOGGER.error("Error while validating the session", e); 126 //we don't stop the thread 127 } 128 long stopTime = System.currentTimeMillis(); 129 if (LOGGER.isDebugEnabled()) { 130 LOGGER.debug("Session validation completed successfully in " + (stopTime - startTime) + " milliseconds."); 131 } 132 } 133 134 public void disableSessionValidation() { 135 if (this.service != null) { 136 this.service.shutdownNow(); 137 } 138 this.enabled = false; 139 } 140}