/*
* 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.
*/
package org.apache.wicket.request;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Exceptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Manages executions of {@link IRequestHandler}s.
*
* @author Matej Knopp
* @author igor.vaynberg
*/
public abstract class RequestHandlerExecutor
{
private static final Logger log = LoggerFactory.getLogger(RequestHandlerExecutor.class);
private IRequestHandler active;
private final List<IRequestHandler> inactiveRequestHandlers = new ArrayList<>();
private IRequestHandler scheduledAfterCurrent = null;
/**
* Get the handler currently executed.
*
* @return active handler
*/
public IRequestHandler getActive()
{
return active;
}
/**
* Execute the given handler.
*
* @param handler handler to be executed
* @return handler to be executed next
* @throws ReplaceHandlerException if another handler should replace the given handler for execution
*/
public IRequestHandler execute(final IRequestHandler handler)
{
active = handler;
try
{
respond(handler);
}
finally
{
active = null;
inactiveRequestHandlers.add(handler);
}
IRequestHandler scheduled = scheduledAfterCurrent;
scheduledAfterCurrent = null;
return scheduled;
}
/**
* Certain exceptions can carry a request handler they wish to be executed, this method tries to
* resolve such a handler given an exception.
*
* @param exception
* @return request handler or null} if one cannot be resolved
* @deprecated
*/
public final IRequestHandler resolveHandler(RuntimeException exception)
{
Args.notNull(exception, "exception");
ReplaceHandlerException replacer = Exceptions.findCause(exception,
ReplaceHandlerException.class);
return replacer != null ? replacer.replacementRequestHandler : null;
}
/**
* Allows the request handler to respond to the request
*
* @param handler
*/
protected abstract void respond(IRequestHandler handler);
/**
* Schedules the handler after the current one
*
* @param handler
*/
public void schedule(final IRequestHandler handler)
{
scheduledAfterCurrent = handler;
}
/**
* @return scheduled request handler after the current one
*/
public IRequestHandler next()
{
return scheduledAfterCurrent;
}
/**
* Replaces all request handlers on the stack with the specified one and executes it. If there
* are any request handlers currently executing (this method is called from inside
* {@link IRequestHandler#respond(IRequestCycle)}) the execution is interrupted via an
* exception.
*
* @param handler
*/
public void replaceAll(final IRequestHandler handler)
{
if (active == null)
{
execute(handler);
}
else
{
throw new ReplaceHandlerException(handler, true);
}
}
/**
* Detaches all request handlers
*/
public void detach()
{
if (active != null)
{
// no request handler should be active at this point
log.warn("request handler is still active.");
inactiveRequestHandlers.add(active);
active = null;
}
for (IRequestHandler handler : inactiveRequestHandlers)
{
try
{
detach(handler);
}
catch (RuntimeException exception)
{
throw exception;
}
catch (Exception exception)
{
log.error("Error detaching RequestHandler", exception);
}
}
}
/**
* Allows the request handler to detach
*
* @param handler
*/
protected abstract void detach(IRequestHandler handler);
/**
* Exception to stop current request handler and execute a new one.
*
* @author Matej Knopp
*/
public static class ReplaceHandlerException extends RuntimeException
{
private static final long serialVersionUID = 1L;
private final boolean removeScheduled;
private final IRequestHandler replacementRequestHandler;
/**
* Construct.
*
* @param replacementRequestHandler
* @param removeScheduled should a possibly scheduled handler be removed
*/
public ReplaceHandlerException(final IRequestHandler replacementRequestHandler,
final boolean removeScheduled)
{
this.replacementRequestHandler = replacementRequestHandler;
this.removeScheduled = removeScheduled;
}
/**
* @deprecated use {@link #getRemoveScheduled()} instead
*/
public boolean isRemoveAll()
{
return removeScheduled;
}
/**
* Should a scheduled handler be removed before replacing the handler
*/
public boolean getRemoveScheduled()
{
return removeScheduled;
}
/**
* @return the RequestHandler that should be used to continue handling the request
*/
public IRequestHandler getReplacementRequestHandler()
{
return replacementRequestHandler;
}
/**
* @see java.lang.Throwable#fillInStackTrace()
*/
@Override
public Throwable fillInStackTrace()
{
// don't do anything here
return null;
}
}
}