org.jasig.cas.web.report.InternalConfigStateController.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.cas.web.report.InternalConfigStateController.java

Source

/*
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo 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 the following location:
 *
 *   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.jasig.cas.web.report;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.PropertyWriter;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.apache.commons.lang3.StringEscapeUtils;
import org.jasig.cas.util.AbstractJacksonBackedJsonSerializer;
import org.jasig.cas.util.JsonSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanIsAbstractException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * Controller that exposes the CAS internal state and beans
 * as JSON. The report is available at <code>/status/config</code>.
 * @author Misagh Moayyed
 * @since 4.1
 */
@Controller("internalConfigController")
@RequestMapping("/status/config")
public final class InternalConfigStateController {

    private static final Logger LOGGER = LoggerFactory.getLogger(InternalConfigStateController.class);

    private static final String VIEW_CONFIG = "monitoring/viewConfig";

    private static final String[] INCLUDE_PACKAGES = new String[] { "org.jasig" };

    @Autowired(required = true)
    private ApplicationContext applicationContext;

    @Autowired(required = true)
    @Qualifier("casProperties")
    private Properties casProperties;

    /**
     * Handle request.
     *
     * @param request the request
     * @param response the response
     * @return the model and view
     * @throws Exception the exception
     */
    @RequestMapping(method = RequestMethod.GET)
    protected ModelAndView handleRequestInternal(final HttpServletRequest request,
            final HttpServletResponse response) throws Exception {

        final Map<String, Object> list = getBeans(this.applicationContext);
        LOGGER.debug("Found [{}] beans to report", list.size());

        final JsonSerializer<Object> serializer = new BeanObjectJsonSerializer();
        final StringBuilder builder = new StringBuilder();
        builder.append('[');

        final Set<Map.Entry<String, Object>> entries = list.entrySet();
        final Iterator<Map.Entry<String, Object>> it = entries.iterator();

        while (it.hasNext()) {
            final Map.Entry<String, Object> entry = it.next();
            final Object obj = entry.getValue();

            final StringWriter writer = new StringWriter();
            writer.append('{');
            writer.append('\"' + entry.getKey() + "\":");
            serializer.toJson(writer, obj);
            writer.append('}');
            builder.append(writer);

            if (it.hasNext()) {
                builder.append(',');
            }
        }
        builder.append(']');
        final ModelAndView mv = new ModelAndView(VIEW_CONFIG);
        final String jsonData = StringEscapeUtils.escapeJson(builder.toString());

        mv.addObject("jsonData", jsonData);
        mv.addObject("properties", casProperties.entrySet());
        return mv;
    }

    private static final class BeanObjectJsonSerializer extends AbstractJacksonBackedJsonSerializer<Object> {
        private static final long serialVersionUID = 691461175315322624L;

        /**
         * Instantiates a new Bean object json serializer.
         */
        public BeanObjectJsonSerializer() {
            super(new MinimalPrettyPrinter());
        }

        @Override
        protected ObjectMapper initializeObjectMapper() {
            final ObjectMapper mapper = super.initializeObjectMapper();

            final FilterProvider filters = new SimpleFilterProvider().addFilter("beanObjectFilter",
                    new CasSimpleBeanObjectFilter());
            mapper.setFilters(filters);

            mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            mapper.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
            mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            mapper.addMixIn(Object.class, CasSimpleBeanObjectFilter.class);
            mapper.disable(SerializationFeature.INDENT_OUTPUT);
            mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

            return mapper;
        }

        @Override
        protected Class<Object> getTypeToSerialize() {
            return Object.class;
        }

        @JsonFilter("beanObjectFilter")
        private static class CasSimpleBeanObjectFilter extends SimpleBeanPropertyFilter {
            private static final Logger LOGGER = getLogger(CasSimpleBeanObjectFilter.class);

            private static final String[] EXCLUDE_FIELDS = new String[] { "logger" };

            @Override
            public void serializeAsField(final Object pojo, final JsonGenerator jgen,
                    final SerializerProvider provider, final PropertyWriter writer) throws Exception {
                try {
                    if (!canSerializeField(pojo, writer)) {
                        return;
                    }
                    super.serializeAsField(pojo, jgen, provider, writer);
                } catch (final Exception e) {
                    LOGGER.debug(e.getMessage());
                }
            }

            /**
             * Can serialize field?
             *
             * @param pojo the pojo
             * @param writer the writer
             * @return the boolean
             */
            private boolean canSerializeField(final Object pojo, final PropertyWriter writer) {
                boolean foundPackage = false;
                final String packageName = pojo.getClass().getPackage().getName();
                for (int i = 0; !foundPackage && i < INCLUDE_PACKAGES.length; i++) {
                    foundPackage = (packageName.startsWith(INCLUDE_PACKAGES[i]));
                }
                if (!foundPackage) {
                    LOGGER.trace("Package [{}] is ignored", packageName);
                    return false;
                }

                boolean foundField = true;
                final String fieldName = writer.getFullName().getSimpleName();
                for (int i = 0; foundField && i < EXCLUDE_FIELDS.length; i++) {
                    foundField = !fieldName.equalsIgnoreCase(EXCLUDE_FIELDS[i]);
                }

                if (!foundField) {
                    LOGGER.trace("Field [{}] is excluded", fieldName);
                    return false;
                }
                return true;
            }
        }
    }

    /**
     * Gets beans in the application context.
     *
     * @param ctx the ctx
     * @return the beans
     */
    private static Map<String, Object> getBeans(final ApplicationContext ctx) {
        final String[] all = BeanFactoryUtils.beanNamesIncludingAncestors(ctx);

        final Map<String, Object> singletons = new HashMap<>(all.length);
        for (final String name : all) {
            try {
                final Object object = ctx.getBean(name);
                if (object != null) {

                    boolean foundPackage = false;
                    final String packageName = object.getClass().getPackage().getName();
                    for (int i = 0; !foundPackage && i < INCLUDE_PACKAGES.length; i++) {
                        foundPackage = (packageName.startsWith(INCLUDE_PACKAGES[i]));
                    }
                    if (foundPackage) {
                        singletons.put(name, object);
                    }

                }
            } catch (final BeanIsAbstractException e) {
                LOGGER.debug("Skipping abstract bean definition. {}", e.getMessage());
            } catch (final Throwable e) {
                LOGGER.trace(e.getMessage(), e);
            }
        }

        return singletons;
    }
}