001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.microservice.resources; 018 019import static org.apache.juneau.bean.html5.HtmlBuilder.*; 020 021import java.io.*; 022import java.util.Map; 023 024import org.apache.juneau.annotation.*; 025import org.apache.juneau.bean.html5.*; 026import org.apache.juneau.collections.*; 027import org.apache.juneau.html.annotation.*; 028import org.apache.juneau.http.annotation.*; 029import org.apache.juneau.http.response.*; 030import org.apache.juneau.rest.annotation.*; 031import org.apache.juneau.rest.servlet.*; 032 033/** 034 * Shows contents of the microservice configuration file. 035 * 036 * <h5 class='section'>See Also:</h5><ul> 037 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauMicroserviceCoreBasics">juneau-microservice-core Basics</a> 038 * </ul> 039 * 040 * @serial exclude 041 */ 042@Rest( 043 path="/config", 044 title="Configuration", 045 description="Contents of configuration file." 046) 047@HtmlDocConfig( 048 navlinks={ 049 "up: request:/..", 050 "api: servlet:/api", 051 "stats: servlet:/stats", 052 "edit: servlet:/edit" 053 }, 054 resolveBodyVars="true" 055) 056@SuppressWarnings("javadoc") 057public class ConfigResource extends BasicRestServlet { 058 private static final long serialVersionUID = 1L; 059 060 @RestGet( 061 path="/", 062 summary="Get config file contents", 063 description="Show contents of config file as a JsonMap.", 064 swagger=@OpSwagger( 065 responses={ 066 "200:{ description:'Config file as a map of map of objects.', example:{'':{defaultKey:'defaultValue'},'Section1':{key1:'val1',key2:123}}}" 067 } 068 ) 069 ) 070 public JsonMap getConfig() { 071 return getContext().getConfig().toMap(); 072 } 073 074 @RestGet( 075 path="/edit", 076 summary="Render form entry page for editing config file", 077 description="Renders a form entry page for editing the raw text of a config file." 078 ) 079 public Form getConfigEditForm() { 080 return form().id("form").action("servlet:/").method("POST").enctype("application/x-www-form-urlencoded").children( 081 div()._class("data").children( 082 table( 083 tr(td().style("text-align:right").children(button("submit","Submit"),button("reset","Reset"))), 084 tr(th().child("Contents")), 085 tr(th().child( 086 textarea().name("contents").rows(40).cols(120).style("white-space:pre;word-wrap:normal;overflow-x:scroll;font-family:monospace;") 087 .text(getContext().getConfig().toString())) 088 ) 089 ) 090 ) 091 ); 092 } 093 094 @RestGet( 095 path="/{section}", 096 summary="Get config file section contents", 097 description="Show contents of config file section as a JsonMap.", 098 swagger=@OpSwagger( 099 responses={ 100 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 101 } 102 ) 103 ) 104 public JsonMap getConfigSection( 105 @Path("section") @Schema(d="Section name in config file.") String section 106 ) throws SectionNotFound { 107 108 return getSection(section); 109 } 110 111 @RestGet( 112 path="/{section}/{key}", 113 summary="Get config file entry value", 114 description="Show value of config file entry as a simple string.", 115 swagger=@OpSwagger( 116 responses={ 117 "200:{ description:'Entry value.', example:'servlet:/htdocs/themes/dark.css'}" 118 } 119 ) 120 ) 121 public String getConfigEntry( 122 @Path("section") @Schema(d="Section name in config file.") String section, 123 @Path("key") @Schema(d="Key name in section.") String key 124 ) throws SectionNotFound { 125 126 return getSection(section).getString(key); 127 } 128 129 @RestPost( 130 path="/", 131 summary="Update config file contents", 132 description="Update the contents of the config file from a FORM post.", 133 swagger=@OpSwagger( 134 responses={ 135 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 136 } 137 ) 138 ) 139 public JsonMap setConfigContentsFormPost( 140 @FormData("contents") @Schema(d="New contents in INI file format.") String contents 141 ) throws Exception { 142 143 return setConfigContents(new StringReader(contents)); 144 } 145 146 @RestPut( 147 path="/", 148 summary="Update config file contents", 149 description="Update the contents of the config file from raw text.", 150 swagger=@OpSwagger( 151 responses={ 152 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 153 } 154 ) 155 ) 156 public JsonMap setConfigContents( 157 @Content @Schema(d="New contents in INI file format.") Reader contents 158 ) throws Exception { 159 160 return getContext().getConfig().load(contents, true).toMap(); 161 } 162 163 @RestPut( 164 path="/{section}", 165 summary="Update config section contents", 166 description="Add or overwrite a config file section.", 167 swagger=@OpSwagger( 168 responses={ 169 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 170 } 171 ) 172 ) 173 public JsonMap setConfigSection( 174 @Path("section") @Schema(d="Section name in config file.") String section, 175 @Content @Schema(d="New contents of config section as a simple map of key/value pairs.") 176 Map<String,Object> contents 177 ) throws Exception { 178 179 getContext().getConfig().setSection(section, null, contents); 180 return getSection(section); 181 } 182 183 @RestPut( 184 path="/{section}/{key}", 185 summary="Update config entry value", 186 description="Add or overwrite a config file entry.", 187 swagger=@OpSwagger( 188 responses={ 189 "200:{ description:'The updated value.', example:'servlet:/htdocs/themes/dark.css'}" 190 } 191 ) 192 ) 193 public String setConfigValue( 194 @Path("section") @Schema(d="Section name in config file.") String section, 195 @Path("key") @Schema(d="Key name in section.") String key, 196 @Content @Schema(d="New value for entry.") String value 197 ) throws SectionNotFound { 198 199 getContext().getConfig().set(section + '/' + key, value); 200 return getSection(section).getString(key); 201 } 202 203 //----------------------------------------------------------------------------------------------------------------- 204 // Helper beans 205 //----------------------------------------------------------------------------------------------------------------- 206 207 @Response @Schema(description="Section not found.") 208 private class SectionNotFound extends NotFound { 209 private static final long serialVersionUID = 1L; 210 211 SectionNotFound() { 212 super("Section not found."); 213 } 214 } 215 216 //----------------------------------------------------------------------------------------------------------------- 217 // Helper methods 218 //----------------------------------------------------------------------------------------------------------------- 219 220 private JsonMap getSection(String name) throws SectionNotFound { 221 return getContext().getConfig().getSection(name).asMap().orElseThrow(SectionNotFound::new); 222 } 223}