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 @Response 059 @Schema(description = "Section not found.") 060 private class SectionNotFound extends NotFound { 061 private static final long serialVersionUID = 1L; 062 063 SectionNotFound() { 064 super("Section not found."); 065 } 066 } 067 068 private static final long serialVersionUID = 1L; 069 070 @RestGet( 071 path="/", 072 summary="Get config file contents", 073 description="Show contents of config file as a JsonMap.", 074 swagger=@OpSwagger( 075 responses={ 076 "200:{ description:'Config file as a map of map of objects.', example:{'':{defaultKey:'defaultValue'},'Section1':{key1:'val1',key2:123}}}" 077 } 078 ) 079 ) 080 public JsonMap getConfig() { 081 return getContext().getConfig().toMap(); 082 } 083 084 @RestGet( 085 path="/edit", 086 summary="Render form entry page for editing config file", 087 description="Renders a form entry page for editing the raw text of a config file." 088 ) 089 public Form getConfigEditForm() { 090 return form().id("form").action("servlet:/").method("POST").enctype("application/x-www-form-urlencoded").children( 091 div()._class("data").children( 092 table( 093 tr(td().style("text-align:right").children(button("submit","Submit"),button("reset","Reset"))), 094 tr(th().child("Contents")), 095 tr(th().child( 096 textarea().name("contents").rows(40).cols(120).style("white-space:pre;word-wrap:normal;overflow-x:scroll;font-family:monospace;") 097 .text(getContext().getConfig().toString())) 098 ) 099 ) 100 ) 101 ); 102 } 103 104 @RestGet( 105 path="/{section}/{key}", 106 summary="Get config file entry value", 107 description="Show value of config file entry as a simple string.", 108 swagger=@OpSwagger( 109 responses={ 110 "200:{ description:'Entry value.', example:'servlet:/htdocs/themes/dark.css'}" 111 } 112 ) 113 ) 114 public String getConfigEntry( 115 @Path("section") @Schema(d="Section name in config file.") String section, 116 @Path("key") @Schema(d="Key name in section.") String key 117 ) throws SectionNotFound { 118 119 return getSection(section).getString(key); 120 } 121 122 @RestGet( 123 path="/{section}", 124 summary="Get config file section contents", 125 description="Show contents of config file section as a JsonMap.", 126 swagger=@OpSwagger( 127 responses={ 128 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 129 } 130 ) 131 ) 132 public JsonMap getConfigSection( 133 @Path("section") @Schema(d="Section name in config file.") String section 134 ) throws SectionNotFound { 135 136 return getSection(section); 137 } 138 139 @RestPut( 140 path="/", 141 summary="Update config file contents", 142 description="Update the contents of the config file from raw text.", 143 swagger=@OpSwagger( 144 responses={ 145 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 146 } 147 ) 148 ) 149 public JsonMap setConfigContents( 150 @Content @Schema(d="New contents in INI file format.") Reader contents 151 ) throws Exception { 152 153 return getContext().getConfig().load(contents, true).toMap(); 154 } 155 156 @RestPost( 157 path="/", 158 summary="Update config file contents", 159 description="Update the contents of the config file from a FORM post.", 160 swagger=@OpSwagger( 161 responses={ 162 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 163 } 164 ) 165 ) 166 public JsonMap setConfigContentsFormPost( 167 @FormData("contents") @Schema(d="New contents in INI file format.") String contents 168 ) throws Exception { 169 170 return setConfigContents(new StringReader(contents)); 171 } 172 173 @RestPut( 174 path="/{section}", 175 summary="Update config section contents", 176 description="Add or overwrite a config file section.", 177 swagger=@OpSwagger( 178 responses={ 179 "200:{ description:'Config file section as a map of objects.', example:{key1:'val1',key2:123}}" 180 } 181 ) 182 ) 183 public JsonMap setConfigSection( 184 @Path("section") @Schema(d="Section name in config file.") String section, 185 @Content @Schema(d="New contents of config section as a simple map of key/value pairs.") 186 Map<String,Object> contents 187 ) throws Exception { 188 189 getContext().getConfig().setSection(section, null, contents); 190 return getSection(section); 191 } 192 @RestPut( 193 path="/{section}/{key}", 194 summary="Update config entry value", 195 description="Add or overwrite a config file entry.", 196 swagger=@OpSwagger( 197 responses={ 198 "200:{ description:'The updated value.', example:'servlet:/htdocs/themes/dark.css'}" 199 } 200 ) 201 ) 202 public String setConfigValue( 203 @Path("section") @Schema(d="Section name in config file.") String section, 204 @Path("key") @Schema(d="Key name in section.") String key, 205 @Content @Schema(d="New value for entry.") String value 206 ) throws SectionNotFound { 207 208 getContext().getConfig().set(section + '/' + key, value); 209 return getSection(section).getString(key); 210 } 211 212 private JsonMap getSection(String name) throws SectionNotFound { 213 return getContext().getConfig().getSection(name).asMap().orElseThrow(SectionNotFound::new); 214 } 215}