/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013-2016 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.jersey.tests.e2e.common;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.Entity;
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.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.annotation.Priority;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.lessThan;
/**
* {@link LoggingFeature} end-to-end tests.
*
* @author Michal Gajdos
*/
@RunWith(Suite.class)
@Suite.SuiteClasses({
LoggingFeatureTest.ClientTest.class,
LoggingFeatureTest.ContainerTest.class,
LoggingFeatureTest.ContainerAutodiscoveryTest.class,
LoggingFeatureTest.FiltersOrderTest.class
})
public class LoggingFeatureTest {
private static final String LOGGER_NAME = "org.glassfish.jersey.logging.feature";
private static final String BINARY_MEDIA_TYPE = "application/binary";
private static final String TEXT_MEDIA_TYPE = MediaType.TEXT_PLAIN;
private static final String ENTITY = "This entity must (not) be logged";
@Path("/")
public static class MyResource {
@GET
@Produces(BINARY_MEDIA_TYPE)
public Response getHeadersAndBinaryPayload() {
return Response
.ok(ENTITY)
.header("001", "First Header Value")
.header("002", "Second Header Value")
.header("003", "Third Header Value")
.header("004", "Fourth Header Value")
.header("005", "Fifth Header Value")
.build();
}
@Path("/text")
@GET
@Produces(TEXT_MEDIA_TYPE)
public Response getHeadersAndTextPayload() {
return Response
.ok(ENTITY)
.header("001", "First Header Value")
.header("002", "Second Header Value")
.header("003", "Third Header Value")
.header("004", "Fourth Header Value")
.header("005", "Fifth Header Value")
.build();
}
@Path("/text")
@POST
@Produces(TEXT_MEDIA_TYPE)
public Response post(String text) {
return Response
.ok(ENTITY)
.build();
}
}
/**
* General client side tests.
*/
public static class ClientTest extends JerseyTest {
@Override
protected Application configure() {
set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
return new ResourceConfig(MyResource.class);
}
@Test
public void testFilterAsClientRequestFilter() throws Exception {
final Response response = target()
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME)))
.request()
.get();
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
}
@Test
public void testOrderOfHeadersOnClient() throws Exception {
final Response response = target()
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME)))
.request()
.get();
assertThat(response.readEntity(String.class), equalTo(ENTITY));
final LogRecord record = getLoggingFilterResponseLogRecord(getLoggedRecords());
final String message = record.getMessage();
int i = 1;
do {
final String h1 = "00" + i++;
final String h2 = "00" + i;
final int i1 = message.indexOf(h1);
final int i2 = message.indexOf(h2);
assertThat("Header " + h1 + " has been logged sooner than header " + h2, i1, lessThan(i2));
} while (i < 5);
}
@Test
public void testVerbosityAnyPayload() throws Exception {
final Response response = target()
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_ANY))
.request()
.get();
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
}
@Test
public void testVerbosityAnyPayloadSetVerbosityAsText() throws Exception {
final Response response = target()
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME)))
.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY, "PAYLOAD_ANY")
.request()
.get();
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
}
@Test
public void testVerbosityTextPayloadBinaryFiltered() throws Exception {
final Response response = target()
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_TEXT))
.request()
.get();
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), not(containsString(ENTITY)));
}
@Test
public void testVerbosityTextPayload() throws Exception {
final Response response = target("/text")
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_TEXT))
.request()
.get();
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
}
@Test
public void testVerbosityHeadersPayload() throws Exception {
final Response response = target()
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.HEADERS_ONLY))
.request()
.get();
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), not(containsString(ENTITY)));
}
@Test
public void testPostedEntityLogged() throws Exception {
final Response response = target("/text")
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME), LoggingFeature.Verbosity.PAYLOAD_TEXT))
.request()
.post(Entity.text(ENTITY));
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(0).getMessage(), containsString(ENTITY));
}
}
/**
* General client side tests.
*/
public static class ContainerTest extends JerseyTest {
@Override
protected Application configure() {
set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());
return new ResourceConfig(MyResource.class)
.register(LoggingFeature.class)
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER, LOGGER_NAME);
}
@Test
public void testLoggingAsContainer() throws Exception {
// Correct response status.
assertThat(target().request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
}
@Test
public void testLoggingAsContainerTextPayload() throws Exception {
// Correct response status.
assertThat(target("/text").request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), containsString(ENTITY));
}
@Test
public void testLoggingAsContainerBinaryPayload() throws Exception {
// Correct response status.
assertThat(target().request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(1).getMessage(), not(containsString(ENTITY)));
}
@Test
public void testPostedEntityLogged() throws Exception {
final Response response = target("/text")
.request()
.post(Entity.text(ENTITY));
// Correct response status.
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(0).getMessage(), containsString(ENTITY));
}
}
/**
* General client side tests.
*/
public static class ContainerAutodiscoveryTest extends JerseyTest {
@Override
protected Application configure() {
set(TestProperties.RECORD_LOG_LEVEL, Level.INFO.intValue());
return new ResourceConfig(MyResource.class)
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER, LOGGER_NAME)
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, "INFO");
}
@Test
public void testFilterAsContainerFilter() throws Exception {
// Correct response status.
assertThat(target().request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
// Check logs for proper id.
assertThat(getLoggingFilterRequestLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
assertThat(getLoggingFilterResponseLogRecord(getLoggedRecords()).getMessage(), containsString("1 *"));
}
}
private static LogRecord getLoggingFilterRequestLogRecord(final List<LogRecord> records) {
return getLoggingFilterLogRecord(records, true);
}
private static LogRecord getLoggingFilterResponseLogRecord(final List<LogRecord> records) {
return getLoggingFilterLogRecord(records, false);
}
private static LogRecord getLoggingFilterLogRecord(final List<LogRecord> records, final boolean requestQuery) {
for (final LogRecord record : getLoggingFilterLogRecord(records)) {
if (record.getMessage().contains(requestQuery ? "request" : "response")) {
return record;
}
}
throw new AssertionError("Unable to find proper log record.");
}
private static List<LogRecord> getLoggingFilterLogRecord(final List<LogRecord> records) {
final List<LogRecord> loggingFilterRecords = new ArrayList<>(records.size());
for (final LogRecord record : records) {
if (record.getLoggerName().startsWith(LOGGER_NAME)) {
loggingFilterRecords.add(record);
}
}
return loggingFilterRecords;
}
public static class FiltersOrderTest extends JerseyTest {
@Priority(1000)
private static class CustomFilter implements ClientRequestFilter, ClientResponseFilter,
ContainerRequestFilter, ContainerResponseFilter {
static final String CUSTOM_HEADER = "custom_header";
@Override
public void filter(final ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(CUSTOM_HEADER, "client/request");
}
@Override
public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add(CUSTOM_HEADER, "client/response");
}
@Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(CUSTOM_HEADER, "container/request");
}
@Override
public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add(CUSTOM_HEADER, "container/response");
}
}
@Override
protected Application configure() {
set(TestProperties.RECORD_LOG_LEVEL, Level.INFO.intValue());
return new ResourceConfig(MyResource.class)
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_NAME, LOGGER_NAME)
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL, "INFO")
.register(CustomFilter.class);
}
@Test
public void testFilterAsContainerFilter() throws Exception {
// Correct response status.
assertThat(target()
.register(CustomFilter.class)
.register(new LoggingFeature(Logger.getLogger(LOGGER_NAME),
Level.INFO,
LoggingFeature.Verbosity.HEADERS_ONLY,
0))
.request().get().getStatus(), is(Response.Status.OK.getStatusCode()));
for (LogRecord record : getLoggedRecords()) {
System.out.println(record.getMessage());
}
// --- client request log entry
// client added header before request has sent (and logged)
assertThat(getLoggedRecords().get(0).getMessage(),
containsString("1 > custom_header: client/request\n"));
// --- container request log entry
// container receives header from client request
assertThat(getLoggedRecords().get(1).getMessage(),
containsString("1 > custom_header: client/request\n"));
// container has added its own header after logging filter logged message
assertThat(getLoggedRecords().get(1).getMessage(),
not(containsString("1 > custom_header: container/request\n")));
// --- container response log entry
// container added header to the response and it was logged
assertThat(getLoggedRecords().get(2).getMessage(),
containsString("1 < custom_header: container/response\n"));
// --- client response log entry
// client received header
assertThat(getLoggedRecords().get(3).getMessage(),
containsString("1 < custom_header: container/response\n"));
assertThat(getLoggedRecords().get(3).getMessage(),
not(containsString("1 < custom_header: client/response\n")));
}
}
}