    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.internal.services;
    016
    017import java.io.IOException;
    018import java.nio.charset.Charset;
    019
    020import org.apache.tapestry5.SymbolConstants;
    021import org.apache.tapestry5.http.services.Dispatcher;
    022import org.apache.tapestry5.http.services.Request;
    023import org.apache.tapestry5.http.services.Response;
    024import org.apache.tapestry5.ioc.annotations.Symbol;
    025import org.apache.tapestry5.json.JSONObject;
    026import org.apache.tapestry5.services.rest.OpenApiDescriptionGenerator;
    027
    028/**
    029 * Recognizes requests where the path matches the value of {@link SymbolConstants#OPENAPI_DESCRIPTION_PATH}
    030 * (<code>/openapi.json</code> by default). Only used if {@link SymbolConstants#PUBLISH_OPENAPI_DEFINITON}
    031 * is set to <code>true</code> (which isn't by default).
    032 * @see OpenApiDescriptionGenerator
    033 */
    034public class OpenApiDescriptionDispatcher implements Dispatcher
    035{
    036
    037    private final String path;
    038    private final OpenApiDescriptionGenerator openApiDescriptionGenerator;
    039    private byte[] cachedDescription;
    040    private final boolean productionMode;
    041
    042    public OpenApiDescriptionDispatcher(@Symbol(SymbolConstants.OPENAPI_DESCRIPTION_PATH) final String path,
    043            final OpenApiDescriptionGenerator openApiDescriptionGenerator,
    044            final @Symbol(SymbolConstants.PRODUCTION_MODE) boolean productionMode)
    045    {
    046        this.path = path;
    047        this.openApiDescriptionGenerator = openApiDescriptionGenerator;
    048        this.productionMode = productionMode;
    049    }
    050
    051    public boolean dispatch(Request request, Response response) throws IOException
    052    {
    053        boolean dispatched = false;
    054        if (path.equals(request.getPath()))
    055        {
    056            final byte[] description = getDescriptionAsByteArray();
    057            response.setContentLength(description.length);
    058            response.getOutputStream("application/json").write(description);
    059            dispatched = true;
    060        }
    061        return dispatched;
    062    }
    063
    064    private byte[] getDescriptionAsByteArray()
    065    {
    066        byte[] bytes;
    067        if (productionMode && cachedDescription != null)
    068        {
    069            bytes = cachedDescription;
    070        }
    071        else
    072        {
    073            bytes = openApiDescriptionGenerator
    074                    .generate(new JSONObject())
    075                    .toCompactString()
    076                    .getBytes(Charset.forName("UTF-8"));
    077            if (productionMode)
    078            {
    079                cachedDescription = bytes;
    080            }
    081        }
    082        return bytes;
    083    }
    084}