    001// Copyright 2021 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007// http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.rest.jackson.modules;
    016
    017import java.util.List;
    018
    019import org.apache.tapestry5.commons.Configuration;
    020import org.apache.tapestry5.commons.MappedConfiguration;
    021import org.apache.tapestry5.commons.OrderedConfiguration;
    022import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
    023import org.apache.tapestry5.http.internal.TapestryHttpInternalConstants;
    024import org.apache.tapestry5.http.services.HttpRequestBodyConverter;
    025import org.apache.tapestry5.http.services.Response;
    026import org.apache.tapestry5.ioc.annotations.Symbol;
    027import org.apache.tapestry5.ioc.services.ChainBuilder;
    028import org.apache.tapestry5.jacksondatabind.services.ObjectMapperSource;
    029import org.apache.tapestry5.rest.jackson.internal.FallbackObjectMapperSource;
    030import org.apache.tapestry5.rest.jackson.internal.JacksonComponentEventResultProcessor;
    031import org.apache.tapestry5.rest.jackson.internal.JacksonHttpRequestBodyConverter;
    032import org.apache.tapestry5.rest.jackson.internal.JacksonOpenApiTypeDescriber;
    033import org.apache.tapestry5.services.ComponentEventResultProcessor;
    034import org.apache.tapestry5.services.rest.MappedEntityManager;
    035import org.apache.tapestry5.services.rest.OpenApiTypeDescriber;
    036
    037import com.fasterxml.jackson.databind.ObjectMapper;
    038import com.github.victools.jsonschema.generator.OptionPreset;
    039import com.github.victools.jsonschema.generator.SchemaGenerator;
    040import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
    041import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
    042import com.github.victools.jsonschema.generator.SchemaVersion;
    043
    044/**
    045 * Defines services and and contributions for the Tapestry integration with Jackson Databind.
    046 * Besides contributing a fallback {@link ObjectMapperSource}, it also creates a
    047 * {@link ComponentEventResultProcessor} for all classes returned by
    048 * {@link MappedEntityManager#getEntities()}.
    049 * @since 5.8.0
    050 */
    051public class RestJacksonModule
    052{
    053
    054    /**
    055     * Contributes {@link FallbackObjectMapperSource} (contribution id <code>Fallback</code>)
    056     * so we guarantee there's always an {@link ObjectMapper} provided for any type.
    057     */
    058    public static void contributeObjectMapperSource(OrderedConfiguration<ObjectMapperSource> configuration)
    059    {
    060        configuration.addInstance("Fallback", FallbackObjectMapperSource.class, "after:*");
    061    }
    062
    063    /**
    064     * Adds a (entity class, JacksonComponentEventResultProcessor) for each entity class
    065     * returned by {@link MappedEntityManager#getEntities()}.
    066     */
    067    @SuppressWarnings({ "rawtypes", "unchecked" })
    068    public static void contributeComponentEventResultProcessor(
    069            MappedConfiguration<Class, ComponentEventResultProcessor> configuration,
    070            MappedEntityManager mappedEntityManager,
    071            Response response,
    072            ObjectMapperSource objectMapperSource,
    073            @Symbol(TapestryHttpSymbolConstants.CHARSET) String outputEncoding)
    074    {
    075        for (Class entityClass : mappedEntityManager.getEntities())
    076        {
    077            configuration.add(entityClass,
    078                    new JacksonComponentEventResultProcessor(entityClass, response, outputEncoding, objectMapperSource));
    079        }
    080    }
    081
    082    /**
    083     * Contributes {@link JacksonHttpRequestBodyConverter} to the {@link HttpRequestBodyConverter} service.
    084     */
    085    public static void contributeHttpRequestBodyConverter(
    086            OrderedConfiguration<HttpRequestBodyConverter> configuration) {
    087        configuration.addInstance("Jackson", JacksonHttpRequestBodyConverter.class);
    088    }
    089
    090    /**
    091     * Builds the {@link ObjectMapperSource} service.
    092     */
    093    public static ObjectMapperSource buildObjectMapperSource(
    094            List<ObjectMapperSource> configuration,
    095            ChainBuilder chainBuilder)
    096    {
    097        return chainBuilder.build(ObjectMapperSource.class, configuration);
    098    }
    099
    100    /**
    101     * Provides the default {@link SchemaGenerator} instance with a default configuration.
    102     */
    103    public static SchemaGenerator buildSchemaGenerator()
    104    {
    105        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(
    106                SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
    107        SchemaGeneratorConfig config = configBuilder.build();
    108        return new SchemaGenerator(config);
    109    }
    110
    111    /**
    112     * Contributes {@link JacksonOpenApiTypeDescriber} to the {@link OpenApiTypeDescriber} service
    113     * to generate J.
    114     */
    115    public static void contributeOpenApiTypeDescriber(OrderedConfiguration<OpenApiTypeDescriber> configuration)
    116    {
    117        configuration.addInstance("Jackson", JacksonOpenApiTypeDescriber.class);
    118    }
    119
    120}