Skip to content

Instantly share code, notes, and snippets.

@chetanmeh
Created August 23, 2013 06:04
Show Gist options
  • Save chetanmeh/6315991 to your computer and use it in GitHub Desktop.
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
/*
* 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