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