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}