Created
August 23, 2013 06:04
-
-
Save chetanmeh/6315991 to your computer and use it in GitHub Desktop.
SessionSynchronizer - Demonstrates an example of using SessionOperationInterceptor https://issues.apache.org/jira/browse/OAK-960
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate; | |
import org.apache.jackrabbit.oak.jcr.delegate.SessionOperationInterceptor; | |
import org.apache.jackrabbit.oak.jcr.operation.SessionOperation; | |
import org.osgi.framework.BundleContext; | |
import org.osgi.framework.ServiceRegistration; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import javax.servlet.Filter; | |
import javax.servlet.FilterChain; | |
import javax.servlet.FilterConfig; | |
import javax.servlet.ServletException; | |
import javax.servlet.ServletRequest; | |
import javax.servlet.ServletResponse; | |
import java.io.IOException; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.Properties; | |
public class SessionSynchronizer implements SessionOperationInterceptor{ | |
/** | |
* The initial value is not populated so as to disable session refresh coordination in | |
* no HTTP request threads as there is no clean boundary to clean up the state | |
*/ | |
private final ThreadLocal<SessionRefreshState> refreshState | |
= new ThreadLocal<SessionRefreshState>(); | |
private final Logger log = LoggerFactory.getLogger(getClass()); | |
private final ServiceRegistration registration; | |
public SessionSynchronizer(BundleContext context){ | |
Properties p = new Properties(); | |
p.setProperty("filter.scope","REQUEST"); | |
registration = context.registerService(Filter.class.getName(),new SessionSynchronizerFilter(),p); | |
} | |
public void close(){ | |
if(registration != null){ | |
registration.unregister(); | |
} | |
} | |
@Override | |
public void before(SessionDelegate delegate, SessionOperation operation) { | |
if(refreshRequired(delegate)){ | |
log.debug("Marking session [{}] to refresh at next operation",delegate); | |
delegate.refreshAtNextAccess(); | |
} | |
} | |
@Override | |
public void after(SessionDelegate delegate, SessionOperation operation) { | |
if(operation.isUpdate()){ | |
sessionUpdated(delegate); | |
log.debug("Detected update in session [{}]",delegate); | |
} | |
} | |
private void sessionUpdated(SessionDelegate delegate){ | |
SessionRefreshState state = refreshState.get(); | |
if(state != null){ | |
state.sessionUpdated(); | |
} | |
} | |
private boolean refreshRequired(SessionDelegate delegate){ | |
SessionRefreshState state = refreshState.get(); | |
if(state != null){ | |
return state.refreshRequired(delegate); | |
} | |
return false; | |
} | |
private class SessionSynchronizerFilter implements Filter { | |
@Override | |
public void init(FilterConfig filterConfig) throws ServletException { | |
} | |
@Override | |
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, | |
FilterChain filterChain) throws IOException, ServletException { | |
try{ | |
refreshState.set(new SessionRefreshState()); | |
filterChain.doFilter(servletRequest,servletResponse); | |
}finally{ | |
refreshState.remove(); | |
} | |
} | |
@Override | |
public void destroy() { | |
} | |
} | |
private static class SessionRefreshState { | |
private int updateCounter = 0; | |
private final Map<Integer,Integer> refreshStatePerSession = new HashMap<Integer, Integer>(); | |
public void sessionUpdated(){ | |
//Increment the counter to indicate that some update has been done | |
//by a session in current thread | |
updateCounter++; | |
} | |
public boolean refreshRequired(SessionDelegate delegate){ | |
//Instead of keeping a hard reference to the sessionDelegate instance | |
//we keep a representative identifier to manage the state | |
Integer sessionId = System.identityHashCode(delegate); | |
Integer oldState = refreshStatePerSession.get(sessionId); | |
Integer currentState = Integer.valueOf(updateCounter); | |
refreshStatePerSession.put(sessionId,currentState); | |
if(oldState == null){ | |
return updateCounter > 0; | |
} | |
//If oldState does not match currentState then some update | |
//would have happened. So return true to indicate client to perform | |
//refresh | |
return !oldState.equals(currentState); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment