001/* 002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 */ 017package org.nuxeo.ecm.platform.web.common.requestcontroller.filter; 018 019import java.io.IOException; 020import java.util.concurrent.locks.Condition; 021import java.util.concurrent.locks.Lock; 022import java.util.concurrent.locks.ReentrantLock; 023 024import javax.servlet.Filter; 025import javax.servlet.FilterChain; 026import javax.servlet.FilterConfig; 027import javax.servlet.ServletException; 028import javax.servlet.ServletRequest; 029import javax.servlet.ServletResponse; 030 031import org.nuxeo.runtime.api.Framework; 032import org.nuxeo.runtime.model.ComponentManager; 033 034/** 035 * Blocks incoming requests when runtime is in standby mode. 036 * 037 * @since 9.2 038 */ 039public class NuxeoStandbyFilter implements Filter { 040 041 protected Controller controller; 042 043 @Override 044 public void init(FilterConfig filterConfig) throws ServletException { 045 controller = new Controller(); 046 new ComponentManager.LifeCycleHandler() { 047 048 @Override 049 public void beforeStop(ComponentManager mgr, boolean isStandby) { 050 controller.onStandby(); 051 } 052 053 @Override 054 public void afterStart(ComponentManager mgr, boolean isResume) { 055 controller.onResumed(); 056 } 057 058 }.install(); 059 } 060 061 @Override 062 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 063 throws IOException, ServletException { 064 controller.onNewRequest(); 065 try { 066 chain.doFilter(request, response); 067 } finally { 068 controller.onRequestEnd(); 069 } 070 } 071 072 @Override 073 public void destroy() { 074 075 } 076 077 protected class Controller { 078 protected final Lock lock = new ReentrantLock(); 079 080 protected final Condition canStandby = lock.newCondition(); 081 082 protected final Condition canProceed = lock.newCondition(); 083 084 protected volatile boolean isStandby = !Framework.getRuntime().getComponentManager().isStarted(); 085 086 protected volatile int inprogress = 0; 087 088 public void onNewRequest() { 089 if (!isStandby) { 090 inprogress += 1; 091 return; 092 } 093 awaitCanProceed(); 094 } 095 096 public void onRequestEnd() { 097 inprogress -= 1; 098 if (inprogress > 0) { 099 return; 100 } 101 lock.lock(); 102 try { 103 canStandby.signal(); 104 } finally { 105 lock.unlock(); 106 } 107 } 108 109 public void onStandby() throws RuntimeException { 110 isStandby = true; 111 if (inprogress > 0) { 112 awaitCanStandby(); 113 } 114 } 115 116 public void onResumed() { 117 isStandby = false; 118 signalBlocked(); 119 } 120 121 protected void awaitCanProceed() throws RuntimeException { 122 lock.lock(); 123 try { 124 canProceed.await(); 125 } catch (InterruptedException cause) { 126 Thread.currentThread().interrupt(); 127 throw new RuntimeException("Interrupted while locking incoming requests", cause); 128 } finally { 129 lock.unlock(); 130 } 131 } 132 133 protected void awaitCanStandby() throws RuntimeException { 134 lock.lock(); 135 try { 136 canStandby.await(); 137 } catch (InterruptedException cause) { 138 Thread.currentThread().interrupt(); 139 throw new RuntimeException("Interrupted while waiting for web requests being drained", cause); 140 } finally { 141 lock.unlock(); 142 } 143 } 144 145 protected void signalBlocked() { 146 lock.lock(); 147 try { 148 canProceed.signalAll(); 149 } finally { 150 lock.unlock(); 151 } 152 } 153 154 } 155 156}