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}