/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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 io.undertow.predicate;
import io.undertow.
UndertowLogger;
import io.undertow.server.
HandlerWrapper;
import io.undertow.server.
HttpHandler;
import io.undertow.server.
HttpServerExchange;
import io.undertow.server.handlers.builder.
HandlerBuilder;
import io.undertow.server.handlers.builder.
PredicatedHandler;
import io.undertow.util.
AttachmentKey;
import java.util.
Collections;
import java.util.
List;
import java.util.
Map;
import java.util.
Set;
import java.util.
TreeMap;
/**
* Handler that can deal with a large number of predicates. chaining together a large number of {@link io.undertow.predicate.PredicatesHandler.Holder}
* instances will make the stack grow to large, so this class is used that can deal with a large number of predicates.
*
* @author Stuart Douglas
*/
public class
PredicatesHandler implements
HttpHandler {
/**
* static done marker. If this is attached to the exchange it will drop out immediately.
*/
public static final
AttachmentKey<
Boolean>
DONE =
AttachmentKey.
create(
Boolean.class);
public static final
AttachmentKey<
Boolean>
RESTART =
AttachmentKey.
create(
Boolean.class);
private volatile
Holder[]
handlers = new
Holder[0];
private volatile
HttpHandler next;
private final boolean
outerHandler;
//non-static, so multiple handlers can co-exist
private final
AttachmentKey<
Integer>
CURRENT_POSITION =
AttachmentKey.
create(
Integer.class);
public
PredicatesHandler(
HttpHandler next) {
this.
next =
next;
this.
outerHandler = true;
}
public
PredicatesHandler(
HttpHandler next, boolean
outerHandler) {
this.
next =
next;
this.
outerHandler =
outerHandler;
}
@
Override
public void
handleRequest(
HttpServerExchange exchange) throws
Exception {
final int
length =
handlers.length;
Integer current =
exchange.
getAttachment(
CURRENT_POSITION);
do {
int
pos;
if (
current == null) {
if (
outerHandler) {
exchange.
removeAttachment(
RESTART);
exchange.
removeAttachment(
DONE);
if (
exchange.
getAttachment(
Predicate.
PREDICATE_CONTEXT) == null) {
exchange.
putAttachment(
Predicate.
PREDICATE_CONTEXT, new
TreeMap<
String,
Object>());
}
}
pos = 0;
} else {
//if it has been marked as done
if (
exchange.
getAttachment(
DONE) != null) {
exchange.
removeAttachment(
CURRENT_POSITION);
next.
handleRequest(
exchange);
return;
}
pos =
current;
}
for (;
pos <
length; ++
pos) {
final
Holder handler =
handlers[
pos];
if (
handler.
predicate.
resolve(
exchange)) {
exchange.
putAttachment(
CURRENT_POSITION,
pos + 1);
handler.
handler.
handleRequest(
exchange);
if(
shouldRestart(
exchange,
current)) {
break;
} else {
return;
}
} else if(
handler.
elseBranch != null) {
exchange.
putAttachment(
CURRENT_POSITION,
pos + 1);
handler.
elseBranch.
handleRequest(
exchange);
if(
shouldRestart(
exchange,
current)) {
break;
} else {
return;
}
}
}
} while (
shouldRestart(
exchange,
current));
next.
handleRequest(
exchange);
}
private boolean
shouldRestart(
HttpServerExchange exchange,
Integer current) {
return
exchange.
getAttachment(
RESTART) != null &&
outerHandler &&
current == null;
}
/**
* Adds a new predicated handler.
* <p>
*
* @param predicate
* @param handlerWrapper
*/
public
PredicatesHandler addPredicatedHandler(final
Predicate predicate, final
HandlerWrapper handlerWrapper, final
HandlerWrapper elseBranch) {
Holder[]
old =
handlers;
Holder[]
handlers = new
Holder[
old.length + 1];
System.
arraycopy(
old, 0,
handlers, 0,
old.length);
HttpHandler elseHandler =
elseBranch != null ?
elseBranch.
wrap(this) : null;
handlers[
old.length] = new
Holder(
predicate,
handlerWrapper.
wrap(this),
elseHandler);
this.
handlers =
handlers;
return this;
}
/**
* Adds a new predicated handler.
* <p>
*
* @param predicate
* @param handlerWrapper
*/
public
PredicatesHandler addPredicatedHandler(final
Predicate predicate, final
HandlerWrapper handlerWrapper) {
this.
addPredicatedHandler(
predicate,
handlerWrapper, null);
return this;
}
public
PredicatesHandler addPredicatedHandler(final
PredicatedHandler handler) {
return
addPredicatedHandler(
handler.
getPredicate(),
handler.
getHandler(),
handler.
getElseHandler());
}
public void
setNext(
HttpHandler next) {
this.
next =
next;
}
public
HttpHandler getNext() {
return
next;
}
private static final class
Holder {
final
Predicate predicate;
final
HttpHandler handler;
final
HttpHandler elseBranch;
private
Holder(
Predicate predicate,
HttpHandler handler,
HttpHandler elseBranch) {
this.
predicate =
predicate;
this.
handler =
handler;
this.
elseBranch =
elseBranch;
}
}
public static final class
DoneHandlerBuilder implements
HandlerBuilder {
@
Override
public
String name() {
return "done";
}
@
Override
public
Map<
String,
Class<?>>
parameters() {
return
Collections.
emptyMap();
}
@
Override
public
Set<
String>
requiredParameters() {
return
Collections.
emptySet();
}
@
Override
public
String defaultParameter() {
return null;
}
@
Override
public
HandlerWrapper build(
Map<
String,
Object>
config) {
return new
HandlerWrapper() {
@
Override
public
HttpHandler wrap(final
HttpHandler handler) {
return new
HttpHandler() {
@
Override
public void
handleRequest(
HttpServerExchange exchange) throws
Exception {
exchange.
putAttachment(
DONE, true);
handler.
handleRequest(
exchange);
}
};
}
};
}
}
public static final class
RestartHandlerBuilder implements
HandlerBuilder {
private static final
AttachmentKey<
Integer>
RESTART_COUNT =
AttachmentKey.
create(
Integer.class);
private static final int
MAX_RESTARTS =
Integer.
getInteger("io.undertow.max_restarts", 1000);
@
Override
public
String name() {
return "restart";
}
@
Override
public
Map<
String,
Class<?>>
parameters() {
return
Collections.
emptyMap();
}
@
Override
public
Set<
String>
requiredParameters() {
return
Collections.
emptySet();
}
@
Override
public
String defaultParameter() {
return null;
}
@
Override
public
HandlerWrapper build(
Map<
String,
Object>
config) {
return new
HandlerWrapper() {
@
Override
public
HttpHandler wrap(final
HttpHandler handler) {
return new
HttpHandler() {
@
Override
public void
handleRequest(
HttpServerExchange exchange) throws
Exception {
Integer restarts =
exchange.
getAttachment(
RESTART_COUNT);
if(
restarts == null) {
restarts = 1;
} else {
restarts++;
}
exchange.
putAttachment(
RESTART_COUNT,
restarts);
if(
restarts >
MAX_RESTARTS) {
throw
UndertowLogger.
ROOT_LOGGER.
maxRestartsExceeded(
MAX_RESTARTS);
}
exchange.
putAttachment(
RESTART, true);
}
};
}
};
}
}
public static class
Wrapper implements
HandlerWrapper {
private final
List<
PredicatedHandler>
handlers;
private final boolean
outerHandler;
public
Wrapper(
List<
PredicatedHandler>
handlers, boolean
outerHandler) {
this.
handlers =
handlers;
this.
outerHandler =
outerHandler;
}
@
Override
public
HttpHandler wrap(
HttpHandler handler) {
PredicatesHandler h = new
PredicatesHandler(
handler,
outerHandler);
for(
PredicatedHandler pred :
handlers) {
h.
addPredicatedHandler(
pred.
getPredicate(),
pred.
getHandler());
}
return
h;
}
}
}