![](/img/trans.png)
[英]Session isn't destroyed just after being expired (invalidated) manually in Spring Security
[英]Spring view scope is not destroyed when session gets invalidated in websphere
I am using Springboot 1.5.9.RELEASE , JSF 2.2.20 (glassfish) , Primefaces 8 running on WebSphere 8.5.5.13 , and I have defined custom view scope, based on the following link:
https://github.com/jneat/spring-jsf
public class ViewScope implements Scope, HttpSessionBindingListener {
private static final long serialVersionUID = 1L;
private static final Log logger = LogFactory.getLog(ViewScope.class);
private final WeakHashMap<HttpSession, Set<ViewScopeViewMapListener>> sessionToListeners = new WeakHashMap<>();
@Override
public Object get(String name, ObjectFactory objectFactory) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
synchronized (viewMap) {
if (viewMap.containsKey(name)) {
return viewMap.get(name);
} else {
logger.trace("Creating bean " + name);
Object object = objectFactory.getObject();
viewMap.put(name, object);
return object;
}
}
}
}
@Override
public String getConversationId() {
return null;
}
/**
* Removing bean from the scope and unregister it destruction callback without executing them.
*
* @see Scope for more details
*/
@Override
public Object remove(String name) {
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
if (viewMap.containsKey(name)) {
Object removed;
synchronized (viewMap) {
if (viewMap.containsKey(name)) {
removed = FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
} else {
return null;
}
}
HttpSession httpSession = (HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(true);
Set<ViewScopeViewMapListener> sessionListeners;
sessionListeners = sessionToListeners.get(httpSession);
if (sessionListeners != null) {
Set<ViewScopeViewMapListener> toRemove = new HashSet<>();
for (ViewScopeViewMapListener listener : sessionListeners) {
if (listener.getName().equals(name)) {
toRemove.add(listener);
FacesContext.getCurrentInstance().getViewRoot().unsubscribeFromViewEvent(PreDestroyViewMapEvent.class, listener);
}
}
synchronized (sessionListeners) {
sessionListeners.removeAll(toRemove);
}
}
return removed;
}
return null;
}
/**
* Register callback to be executed only on the whole scope destroying (not single object).
*
* @see Scope for more details
*/
@Override
public void registerDestructionCallback(String name, Runnable callback) {
logger.trace("registerDestructionCallback for bean " + name);
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
ViewScopeViewMapListener listener = new ViewScopeViewMapListener(viewRoot, name, callback, this);
viewRoot.subscribeToViewEvent(PreDestroyViewMapEvent.class, listener);
HttpSession httpSession = (HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(true);
final Set<ViewScopeViewMapListener> sessionListeners;
if (sessionToListeners.containsKey(httpSession)) {
sessionListeners = sessionToListeners.get(httpSession);
} else {
synchronized (sessionToListeners) {
if (sessionToListeners.containsKey(httpSession)) {
sessionListeners = sessionToListeners.get(httpSession);
} else {
sessionListeners = new HashSet<>();
sessionToListeners.put(httpSession, sessionListeners);
}
}
}
synchronized (sessionListeners) {
sessionListeners.add(listener);
}
if (!FacesContext.getCurrentInstance().getExternalContext().getSessionMap().containsKey("sessionBindingListener")) {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("sessionBindingListener", this);
}
}
@Override
public Object resolveContextualObject(String key) {
return null;
}
@Override
public void valueBound(HttpSessionBindingEvent event) {
logger.trace("Session event bound " + event.getName());
}
/**
* Seems like it called after our listeners were unbounded from HTTP session.
* Looks like view scope is destroyed. But should we call callback or not is a big question.
*
* @see HttpSessionBindingListener for more details
*/
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
logger.trace("Session event unbound " + event.getName());
final Set<ViewScopeViewMapListener> listeners;
synchronized (sessionToListeners) {
if (sessionToListeners.containsKey(event.getSession())) {
listeners = sessionToListeners.get(event.getSession());
sessionToListeners.remove(event.getSession());
} else {
listeners = null;
}
}
if (listeners != null) {
// I just hope that JSF context already done this job
for (ViewScopeViewMapListener listener : listeners) {
// As long as our callbacks can run only once - this is not such big deal
listener.doCallback();
}
}
}
/**
* Will remove the listener from the session set and unregister it from UIViewRoot.
*/
public void unregisterListener(ViewScopeViewMapListener listener) {
logger.debug("Removing listener from map");
HttpSession httpSession = (HttpSession)FacesContext.getCurrentInstance().getExternalContext().getSession(false);
FacesContext.getCurrentInstance().getViewRoot().unsubscribeFromViewEvent(PreDestroyViewMapEvent.class, listener);
if (httpSession != null) {
synchronized (sessionToListeners) {
if (sessionToListeners.containsKey(httpSession)) {
sessionToListeners.get(httpSession).remove(listener);
}
}
}
}
}
- ViewScopeViewMapListener:
public class ViewScopeViewMapListener implements ViewMapListener {
private static final Log logger = LogFactory.getLog(ViewScope.class);
private final String name;
private final Runnable callback;
private boolean callbackCalled = false;
private final WeakReference<UIViewRoot> uiViewRootWeakReference;
private final ViewScope viewScope;
public ViewScopeViewMapListener(UIViewRoot root, String name, Runnable callback, ViewScope viewScope) {
this.name = name;
this.callback = callback;
this.uiViewRootWeakReference = new WeakReference<>(root);
this.viewScope = viewScope;
}
public synchronized void doCallback() {
logger.trace("Going call callback for bean " + name);
if (!callbackCalled) {
try {
callback.run();
} finally {
callbackCalled = true;
}
}
}
public String getName() {
return name;
}
@Override
public boolean isListenerForSource(Object source) {
return (source == uiViewRootWeakReference.get());
}
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PreDestroyViewMapEvent) {
logger.trace("Going call callback for bean " + name);
doCallback();
viewScope.unregisterListener(this);
}
}
}
- SpringScopeView 注釋:
@Qualifier
@Scope("view")
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringScopeView {
}
- 定義自定義 scope:
@Configuration
public class MyViewScope extends CustomScopeConfigurer {
public InitViewScope() {
log.info("Init ViewScope");
Map<String, Object> map = new HashMap<>();
map.put("view", new ViewScope());
super.setScopes(map);
}
}
- 查看 scope 豆樣品:
@Component("employeeRequest")
@SpringScopeView
public class EmployeeRequestBean implements Serializable {
private static final long serialVersionUID = 6609775672949354713L;
@Autowired
private CurrentUserBean currentUserBean;
@Autowired
private RequestRepository requestRepository;
@Autowired
private ActionRepository actionRepository;
@Autowired
private SMSServiceClient smsServiceClient;
private List<AttachmentDTO> uploadedFilesList = new ArrayList<AttachmentDTO>();
private Request request;
private Action action;
@PostConstruct
public void init() {
try {
String selectedRequestId = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
.get("selectedRequestId");
if (StringUtils.isBlank(selectedRequestId))
return;
request = requestRepository.getOne(Long.parseLong(selectedRequestId));
showRequest();
} catch (Exception e) {
log.error("Exception in init EmployeeRequestBean: ", e);
throw new RuntimeException(e);
}
}
@PreDestroy
public void preDestroy() {
System.err.println("####### EmployeeRequestBean DESTROYED #########");
}
}
問題:執行注銷時,所有 session scope bean 都被銷毀並調用了@PreDestroy
,而視圖@PreDestroy
bean 沒有被調用!
此問題僅在 WebSphere 8.5.5.13 上發生,但在 tomcat 9 上運行相同的應用程序時,一切正常。
@PreDestroy
很棘手。 你看到這個問題了嗎? 他們說,最終,它應該在某個時候被觸發。 我認為,這對你來說不合適。
該怎么辦?
避免使用@PreDestroy
,我正在盡我所能不使用它們。 我討厭這樣的問題 - 有時會觸發某些東西,即使它今天有效,升級也會破壞它......
通常,我所做的是跟蹤已啟動和正在運行的會話。 其他一切都可以關閉 - 通過調用方法。 這樣我就可以進行測試、集成測試,還可以跟蹤活躍用戶的數量。 像往常一樣 - 更多的工作,更好的控制。
對我來說,JSF 和 Spring 是分開的。 @PreDestroy
是 Spring 功能。 我的猜測是,有些東西一直引用 object,這就是它沒有被破壞的原因。 而那個 jneat 與那個是分開的。 這里證明的是,對於純 Spring bean,它可以工作......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.