/**
* 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.cxf.systest.jaxrs;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NameBinding;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
import org.apache.cxf.jaxrs.utils.InjectionUtils;
import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
public class BookServer20 extends AbstractBusTestServerBase {
public static final String PORT = allocatePort(BookServer20.class);
org.apache.cxf.endpoint.Server server;
protected void run() {
Bus bus = BusFactory.getDefaultBus();
setBus(bus);
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setBus(bus);
sf.setResourceClasses(BookStore.class);
List<Object> providers = new ArrayList<>();
providers.add(new PreMatchContainerRequestFilter2());
providers.add(new PreMatchContainerRequestFilter());
providers.add(new PostMatchContainerResponseFilter());
providers.add(new PostMatchContainerResponseFilter3());
providers.add(new PostMatchContainerResponseFilter2());
providers.add(new CustomReaderBoundInterceptor());
providers.add(new CustomReaderInterceptor());
providers.add(new CustomWriterInterceptor());
providers.add(new CustomDynamicFeature());
providers.add(new PostMatchContainerRequestFilter());
providers.add(new FaultyContainerRequestFilter());
providers.add(new PreMatchReplaceStreamOrAddress());
providers.add(new ServerTestFeature());
providers.add(new JacksonJaxbJsonProvider());
sf.setProviders(providers);
sf.setResourceProvider(BookStore.class,
new SingletonResourceProvider(new BookStore(), true));
sf.setAddress("http://localhost:" + PORT + "/");
server = sf.create();
BusFactory.setDefaultBus(null);
BusFactory.setThreadDefaultBus(null);
}
public void tearDown() throws Exception {
server.stop();
server.destroy();
server = null;
}
public static void main(String[] args) {
try {
BookServer20 s = new BookServer20();
s.start();
} catch (Exception ex) {
ex.printStackTrace();
System.exit(-1);
} finally {
System.out.println("done!");
}
}
@PreMatching
@Priority(1)
private static class PreMatchContainerRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext context) throws IOException {
UriInfo ui = context.getUriInfo();
String path = ui.getPath(false);
if (context.getMethod().equals("POST")
&& "bookstore/bookheaders/simple".equals(path) && !context.hasEntity()) {
byte[] bytes = StringUtils.toBytesUTF8("<Book><name>Book</name><id>126</id></Book>");
context.getHeaders().putSingle(HttpHeaders.CONTENT_LENGTH, Integer.toString(bytes.length));
context.getHeaders().putSingle("Content-Type", "application/xml");
context.getHeaders().putSingle("EmptyRequestStreamDetected", "true");
context.setEntityStream(new ByteArrayInputStream(bytes));
}
if ("true".equals(context.getProperty("DynamicPrematchingFilter"))) {
throw new RuntimeException();
}
context.setProperty("FirstPrematchingFilter", "true");
if ("wrongpath".equals(path)) {
context.setRequestUri(URI.create("/bookstore/bookheaders/simple"));
} else if ("throwException".equals(path)) {
context.setProperty("filterexception", "prematch");
throw new InternalServerErrorException(
Response.status(500).type("text/plain")
.entity("Prematch filter error").build());
}
MediaType mt = context.getMediaType();
if (mt != null && mt.toString().equals("text/xml")) {
String method = context.getMethod();
if ("PUT".equals(method)) {
context.setMethod("POST");
}
context.getHeaders().putSingle("Content-Type", "application/xml");
} else {
String newMt = context.getHeaderString("newmediatype");
if (newMt != null) {
context.getHeaders().putSingle("Content-Type", newMt);
}
}
List<MediaType> acceptTypes = context.getAcceptableMediaTypes();
if (acceptTypes.size() == 1 && acceptTypes.get(0).toString().equals("text/mistypedxml")) {
context.getHeaders().putSingle("Accept", "text/xml");
}
}
}
@PreMatching
@Priority(3)
private static class PreMatchContainerRequestFilter2 implements ContainerRequestFilter {
@Context
private HttpServletRequest servletRequest;
@Override
public void filter(ContainerRequestContext context) throws IOException {
if (!"true".equals(context.getProperty("FirstPrematchingFilter"))
|| !"true".equals(context.getProperty("DynamicPrematchingFilter"))
|| !"true".equals(servletRequest.getAttribute("FirstPrematchingFilter"))
|| !"true".equals(servletRequest.getAttribute("DynamicPrematchingFilter"))) {
throw new RuntimeException();
}
context.getHeaders().add("BOOK", "12");
}
}
@PreMatching
private static class PreMatchReplaceStreamOrAddress implements ContainerRequestFilter {
@Context
private UriInfo ui;
@Override
public void filter(ContainerRequestContext context) throws IOException {
String path = ui.getPath();
if (path.endsWith("books/checkN")) {
URI requestURI = URI.create(path.replace("N", "2"));
context.setRequestUri(requestURI);
String body = IOUtils.readStringFromStream(context.getEntityStream());
if (!"s".equals(body)) {
throw new RuntimeException();
}
replaceStream(context);
} else if (path.endsWith("books/check2")) {
replaceStream(context);
} else if (path.endsWith("books/checkNQuery")) {
URI requestURI = URI.create(path.replace("NQuery", "2?a=b"));
context.setRequestUri(requestURI);
replaceStream(context);
}
}
private void replaceStream(ContainerRequestContext context) {
InputStream is = new ByteArrayInputStream("123".getBytes());
context.setEntityStream(is);
}
}
@PreMatching
@Priority(2)
private static class PreMatchDynamicContainerRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext context) throws IOException {
if (!"true".equals(context.getProperty("FirstPrematchingFilter"))) {
throw new RuntimeException();
}
context.setProperty("DynamicPrematchingFilter", "true");
}
}
@CustomHeaderAdded
private static class PostMatchContainerRequestFilter implements ContainerRequestFilter {
@Context
private UriInfo ui;
@Override
public void filter(ContainerRequestContext context) throws IOException {
if (ui.getQueryParameters().getFirst("throwException") != null) {
context.setProperty("filterexception", "postmatch");
throw new InternalServerErrorException(
Response.status(500).type("text/plain")
.entity("Postmatch filter error").build());
}
String value = context.getHeaders().getFirst("Book");
if (value != null) {
context.getHeaders().addFirst("Book", value + "3");
}
}
}
@Faulty
@CustomHeaderAdded
private static class FaultyContainerRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext context) throws IOException {
throw new RuntimeException();
}
}
@Priority(3)
public static class PostMatchContainerResponseFilter implements ContainerResponseFilter {
@Context
private ResourceInfo rInfo;
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
String ct = responseContext.getMediaType().toString();
if (requestContext.getProperty("filterexception") != null) {
if (!"text/plain".equals(ct)) {
throw new RuntimeException();
}
responseContext.getHeaders().putSingle("FilterException",
requestContext.getProperty("filterexception"));
}
Object entity = responseContext.getEntity();
Type entityType = responseContext.getEntityType();
if (entity instanceof GenericHandler && InjectionUtils.getActualType(entityType) == Book.class) {
ct += ";charset=ISO-8859-1";
if ("getGenericBook2".equals(rInfo.getResourceMethod().getName())) {
Annotation[] anns = responseContext.getEntityAnnotations();
if (anns.length == 4 && anns[3].annotationType() == Context.class) {
responseContext.getHeaders().addFirst("Annotations", "OK");
}
} else {
responseContext.setEntity(new Book("book", 124L));
}
} else {
ct += ";charset=";
}
responseContext.getHeaders().putSingle("Content-Type", ct);
responseContext.getHeaders().add("Response", "OK");
}
}
@Priority(1)
public static class PostMatchContainerResponseFilter2 implements ContainerResponseFilter {
@Context
private ResourceInfo ri;
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
if (ri.getResourceMethod() != null
&& "addBook2".equals(ri.getResourceMethod().getName())) {
return;
}
if (!responseContext.getHeaders().containsKey("Response")) {
throw new RuntimeException();
}
if ((!responseContext.getHeaders().containsKey("DynamicResponse")
|| !responseContext.getHeaders().containsKey("DynamicResponse2"))
&& !"Prematch filter error".equals(responseContext.getEntity())) {
throw new RuntimeException();
}
responseContext.getHeaders().add("Response2", "OK2");
}
}
@Priority(4)
@CustomHeaderAdded
@PostMatchMode
public static class PostMatchContainerResponseFilter3 implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
responseContext.getHeaders().add("Custom", "custom");
if (!responseContext.getEntity().equals("Postmatch filter error")) {
Book book = (Book)responseContext.getEntity();
responseContext.setEntity(new Book(book.getName(), 1 + book.getId()), null, null);
}
}
}
public static class PostMatchDynamicContainerRequestResponseFilter
implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
if (!responseContext.getHeaders().containsKey("Response")) {
throw new RuntimeException();
}
responseContext.getHeaders().add("DynamicResponse2", "Dynamic2");
}
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
throw new RuntimeException();
}
}
public static class PostMatchDynamicEchoBookFilter implements ContainerResponseFilter {
private int supplement;
public PostMatchDynamicEchoBookFilter(int supplement) {
this.supplement = supplement;
}
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
Book book = (Book)responseContext.getEntity();
responseContext.setEntity(new Book(book.getName(), book.getId() + supplement));
}
}
@Priority(2)
public static class PostMatchDynamicContainerResponseFilter
implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
if (!responseContext.getHeaders().containsKey("Response")) {
throw new RuntimeException();
}
responseContext.getHeaders().add("DynamicResponse", "Dynamic");
}
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@NameBinding
public @interface CustomHeaderAdded {
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@NameBinding
public @interface CustomHeaderAddedAsync {
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@NameBinding
public @interface PostMatchMode {
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
@NameBinding
public @interface Faulty {
}
@Priority(1)
public static class CustomReaderInterceptor implements ReaderInterceptor {
@Context
private ResourceInfo ri;
@Override
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException,
WebApplicationException {
if (ri.getResourceClass() == BookStore.class) {
context.getHeaders().add("ServerReaderInterceptor", "serverRead");
}
return context.proceed();
}
}
@Priority(2)
@CustomHeaderAddedAsync
public static class CustomReaderBoundInterceptor implements ReaderInterceptor {
@Context
private ResourceInfo ri;
@Context
private UriInfo ui;
@Override
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException,
WebApplicationException {
if (ri.getResourceClass() == BookStore.class) {
String serverRead = context.getHeaders().getFirst("ServerReaderInterceptor");
if (serverRead == null || !"serverRead".equals(serverRead)) {
throw new RuntimeException();
}
if (ui.getPath().endsWith("/async")) {
context.getHeaders().putSingle("ServerReaderInterceptor", "serverReadAsync");
}
}
return context.proceed();
}
}
public static class CustomWriterInterceptor implements WriterInterceptor {
@Context
private HttpServletResponse response;
@Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
context.getHeaders().add("ServerWriterInterceptor", "serverWrite");
context.getHeaders().putSingle("ServerWriterInterceptor2", "serverWrite2");
response.addHeader("ServerWriterInterceptorHttpResponse", "serverWriteHttpResponse");
String ct = context.getHeaders().getFirst("Content-Type").toString();
if (!ct.endsWith("ISO-8859-1")) {
ct += "us-ascii";
}
context.setMediaType(MediaType.valueOf(ct));
context.proceed();
}
}
public static class CustomDynamicFeature implements DynamicFeature {
private static final ContainerResponseFilter RESPONSE_FILTER =
new PostMatchDynamicContainerResponseFilter();
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext configurable) {
configurable.register(new PreMatchDynamicContainerRequestFilter());
configurable.register(RESPONSE_FILTER);
Map<Class<?>, Integer> contracts = new HashMap<>();
contracts.put(ContainerResponseFilter.class, 2);
configurable.register(new PostMatchDynamicContainerRequestResponseFilter(),
contracts);
Method m = resourceInfo.getResourceMethod();
if ("echoBookElement".equals(m.getName())) {
Class<?> paramType = m.getParameterTypes()[0];
if (paramType == Book.class) {
configurable.register(new PostMatchDynamicEchoBookFilter(2));
}
}
}
}
private static class ServerTestFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new GenericHandlerWriter());
return true;
}
}
}