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}