com.nesscomputing.jersey.filter.BodySizeLimitResourceFilterFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.nesscomputing.jersey.filter.BodySizeLimitResourceFilterFactory.java

Source

/**
 * Copyright (C) 2012 Ness Computing, Inc.
 *
 * 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 com.nesscomputing.jersey.filter;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.sun.jersey.api.model.AbstractMethod;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import com.sun.jersey.spi.container.ResourceFilter;
import com.sun.jersey.spi.container.ResourceFilterFactory;

import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.lang3.ObjectUtils;

import com.nesscomputing.config.Config;
import com.nesscomputing.jersey.util.MaxSizeInputStream;
import com.nesscomputing.logging.Log;

/**
 * A {@link ResourceFilterFactory} which discovers all resource methods.  If they take
 * an input stream, they will wrap it in {@link MaxSizeInputStream} with the configured
 * size.  Configuration keys, with the highest priority last:
 * <table border=1>
 * <tr><th>Key</th><th>Default Value</th><th>Purpose</th></tr>
 * <tr><td>ness.filter.max-body-size</td><td>1MB</td><td>Global restriction / default</td></tr>
 * <tr><td>ness.filter.max-body-size.<i>full-class-name</i></td><td>-</td><td>Restricts all resource methods in the class</td></tr>
 * <tr><td>ness.filter.max-body-size.<i>full-class-name</i>.<i>method-name</i></td><td>Restricts a particular method</td></tr>
 * </table>
 */
@Singleton
public class BodySizeLimitResourceFilterFactory implements ResourceFilterFactory {

    private static final Log LOG = Log.findLog();

    private final Config config;
    private final long defaultSizeLimit;

    @Inject
    BodySizeLimitResourceFilterFactory(Config config) {
        this.config = config;
        NessJerseyFiltersConfig filterConfig = config.getBean(NessJerseyFiltersConfig.class);

        defaultSizeLimit = filterConfig.getMaxBodySize();

    }

    @Override
    public List<ResourceFilter> create(AbstractMethod am) {
        BodySizeLimit annotation = ObjectUtils.firstNonNull(am.getAnnotation(BodySizeLimit.class),
                am.getResource().getAnnotation(BodySizeLimit.class));

        final AbstractConfiguration ac = config.getConfiguration();
        final Method realMethod = am.getMethod();
        Class<?> resourceClass = am.getResource().getResourceClass();
        Map<String, Long> foundValues = Maps.newLinkedHashMap();
        do {
            final String classConfigKey = "ness.filter.max-body-size." + resourceClass.getName();
            try {
                resourceClass.getMethod(realMethod.getName(), realMethod.getParameterTypes());
            } catch (NoSuchMethodException | SecurityException e) {
                LOG.trace(e);
                continue;
            }
            final String methodKey = classConfigKey + "." + realMethod.getName();
            foundValues.put(methodKey, ac.getLong(methodKey, null));
            foundValues.put(classConfigKey, ac.getLong(classConfigKey, null));
        } while ((resourceClass = resourceClass.getSuperclass()) != null);

        if (annotation != null) {
            foundValues.put("annotation " + annotation, annotation.value());
        }
        foundValues.put("default", defaultSizeLimit);

        for (Entry<String, Long> e : foundValues.entrySet()) {
            final Long value = e.getValue();
            if (value != null) {

                String message = "[%s] => %s";
                Object[] args = {
                        Joiner.on("], [").withKeyValueSeparator(" = ").useForNull("null").join(foundValues),
                        value };

                if (value != defaultSizeLimit || annotation != null) {
                    LOG.debug(message, args);
                } else {
                    LOG.trace(message, args);
                }

                return Collections.<ResourceFilter>singletonList(new Filter(value));
            }
        }

        throw new IllegalStateException("No value found for " + am + ": " + foundValues);
    }

    private static class Filter implements ResourceFilter, ContainerRequestFilter {
        private final long maxSize;

        public Filter(long maxSize) {
            this.maxSize = maxSize;
        }

        @Override
        public ContainerRequestFilter getRequestFilter() {
            return this;
        }

        @Override
        public ContainerResponseFilter getResponseFilter() {
            return null;
        }

        @Override
        public ContainerRequest filter(ContainerRequest request) {
            InputStream entityInputStream = request.getEntityInputStream();
            if (entityInputStream != null) {
                request.setEntityInputStream(new MaxSizeInputStream(entityInputStream, maxSize));
            }
            return request;
        }
    }
}