001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.request; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025/** 026 * Manages executions of {@link IRequestHandler}s. 027 * 028 * @author Matej Knopp 029 * @author igor.vaynberg 030 */ 031public abstract class RequestHandlerExecutor 032{ 033 private static final Logger log = LoggerFactory.getLogger(RequestHandlerExecutor.class); 034 035 private IRequestHandler active; 036 037 private final List<IRequestHandler> inactiveRequestHandlers = new ArrayList<>(); 038 039 private IRequestHandler scheduledAfterCurrent = null; 040 041 /** 042 * Get the handler currently executed. 043 * 044 * @return active handler 045 */ 046 public IRequestHandler getActive() 047 { 048 return active; 049 } 050 051 /** 052 * Execute the given handler. 053 * 054 * @param handler handler to be executed 055 * @return handler to be executed next 056 * @throws ReplaceHandlerException if another handler should replace the given handler for execution 057 */ 058 public IRequestHandler execute(final IRequestHandler handler) 059 { 060 active = handler; 061 try 062 { 063 respond(handler); 064 } 065 finally 066 { 067 active = null; 068 inactiveRequestHandlers.add(handler); 069 } 070 071 IRequestHandler scheduled = scheduledAfterCurrent; 072 scheduledAfterCurrent = null; 073 return scheduled; 074 } 075 076 /** 077 * Allows the request handler to respond to the request 078 * 079 * @param handler 080 */ 081 protected abstract void respond(IRequestHandler handler); 082 083 /** 084 * Schedules the handler after the current one 085 * 086 * @param handler 087 */ 088 public void schedule(final IRequestHandler handler) 089 { 090 scheduledAfterCurrent = handler; 091 } 092 093 /** 094 * @return scheduled request handler after the current one 095 */ 096 public IRequestHandler next() 097 { 098 return scheduledAfterCurrent; 099 } 100 101 /** 102 * Replaces all request handlers on the stack with the specified one and executes it. If there 103 * are any request handlers currently executing (this method is called from inside 104 * {@link IRequestHandler#respond(IRequestCycle)}) the execution is interrupted via an 105 * exception. 106 * 107 * @param handler 108 */ 109 public void replaceAll(final IRequestHandler handler) 110 { 111 if (active == null) 112 { 113 execute(handler); 114 } 115 else 116 { 117 throw new ReplaceHandlerException(handler, true); 118 } 119 } 120 121 /** 122 * Detaches all request handlers 123 */ 124 public void detach() 125 { 126 if (active != null) 127 { 128 // no request handler should be active at this point 129 log.warn("request handler is still active."); 130 131 inactiveRequestHandlers.add(active); 132 active = null; 133 } 134 135 RuntimeException rethrow = null;; 136 for (IRequestHandler handler : inactiveRequestHandlers) 137 { 138 try 139 { 140 detach(handler); 141 } 142 catch (Exception exception) 143 { 144 if (rethrow == null && exception instanceof RuntimeException) { 145 rethrow = (RuntimeException) exception; 146 } else { 147 log.error("Error detaching RequestHandler", exception); 148 } 149 } 150 } 151 if (rethrow != null) { 152 // WICKET-6001 runtime exceptions are rethrown 153 // TODO obsolete should component-queueing be removed 154 throw rethrow; 155 } 156 } 157 158 /** 159 * Allows the request handler to detach 160 * 161 * @param handler 162 */ 163 protected abstract void detach(IRequestHandler handler); 164 165 /** 166 * Exception to stop current request handler and execute a new one. 167 * 168 * @author Matej Knopp 169 */ 170 public static class ReplaceHandlerException extends RuntimeException 171 { 172 private static final long serialVersionUID = 1L; 173 174 private final boolean removeScheduled; 175 176 private final IRequestHandler replacementRequestHandler; 177 178 /** 179 * Construct. 180 * 181 * @param replacementRequestHandler 182 * @param removeScheduled should a possibly scheduled handler be removed 183 */ 184 public ReplaceHandlerException(final IRequestHandler replacementRequestHandler, 185 final boolean removeScheduled) 186 { 187 this.replacementRequestHandler = replacementRequestHandler; 188 this.removeScheduled = removeScheduled; 189 } 190 191 /** 192 * Should a scheduled handler be removed before replacing the handler 193 */ 194 public boolean getRemoveScheduled() 195 { 196 return removeScheduled; 197 } 198 199 /** 200 * @return the RequestHandler that should be used to continue handling the request 201 */ 202 public IRequestHandler getReplacementRequestHandler() 203 { 204 return replacementRequestHandler; 205 } 206 207 /** 208 * @see java.lang.Throwable#fillInStackTrace() 209 */ 210 @Override 211 public Throwable fillInStackTrace() 212 { 213 // don't do anything here 214 return null; 215 } 216 } 217}