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.config.store; 014 015import static org.apache.juneau.common.internal.StringUtils.*; 016 017import java.io.*; 018import java.lang.annotation.*; 019import java.lang.reflect.*; 020import java.util.concurrent.*; 021 022import org.apache.juneau.*; 023import org.apache.juneau.common.internal.*; 024import org.apache.juneau.internal.*; 025import org.apache.juneau.utils.*; 026 027/** 028 * Classpath-based storage location for configuration files. 029 * 030 * <p> 031 * Looks inside the JVM classpath for configuration files. 032 * 033 * <p> 034 * Configuration files retrieved from the classpath can be modified but not persisted. 035 * 036 * <h5 class='section'>Notes:</h5><ul> 037 * <li class='note'>This class is thread safe and reusable. 038 * </ul> 039 */ 040public class ClasspathStore extends ConfigStore { 041 042 //------------------------------------------------------------------------------------------------------------------- 043 // Static 044 //------------------------------------------------------------------------------------------------------------------- 045 046 /** Default memory store, all default values.*/ 047 public static final ClasspathStore DEFAULT = ClasspathStore.create().build(); 048 049 /** 050 * Creates a new builder for this object. 051 * 052 * @return A new builder. 053 */ 054 public static Builder create() { 055 return new Builder(); 056 } 057 058 //------------------------------------------------------------------------------------------------------------------- 059 // Builder 060 //------------------------------------------------------------------------------------------------------------------- 061 062 /** 063 * Builder class. 064 */ 065 @FluentSetters 066 public static class Builder extends ConfigStore.Builder { 067 068 /** 069 * Constructor, default settings. 070 */ 071 protected Builder() { 072 } 073 074 /** 075 * Copy constructor. 076 * 077 * @param copyFrom The bean to copy from. 078 */ 079 protected Builder(ClasspathStore copyFrom) { 080 super(copyFrom); 081 type(copyFrom.getClass()); 082 } 083 084 /** 085 * Copy constructor. 086 * 087 * @param copyFrom The builder to copy from. 088 */ 089 protected Builder(Builder copyFrom) { 090 super(copyFrom); 091 } 092 093 @Override /* Context.Builder */ 094 public Builder copy() { 095 return new Builder(this); 096 } 097 098 @Override /* Context.Builder */ 099 public ClasspathStore build() { 100 return build(ClasspathStore.class); 101 } 102 103 //----------------------------------------------------------------------------------------------------------------- 104 // Properties 105 //----------------------------------------------------------------------------------------------------------------- 106 107 // <FluentSetters> 108 109 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 110 public Builder annotations(Annotation...values) { 111 super.annotations(values); 112 return this; 113 } 114 115 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 116 public Builder apply(AnnotationWorkList work) { 117 super.apply(work); 118 return this; 119 } 120 121 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 122 public Builder applyAnnotations(java.lang.Class<?>...fromClasses) { 123 super.applyAnnotations(fromClasses); 124 return this; 125 } 126 127 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 128 public Builder applyAnnotations(Method...fromMethods) { 129 super.applyAnnotations(fromMethods); 130 return this; 131 } 132 133 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 134 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 135 super.cache(value); 136 return this; 137 } 138 139 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 140 public Builder debug() { 141 super.debug(); 142 return this; 143 } 144 145 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 146 public Builder debug(boolean value) { 147 super.debug(value); 148 return this; 149 } 150 151 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 152 public Builder impl(Context value) { 153 super.impl(value); 154 return this; 155 } 156 157 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 158 public Builder type(Class<? extends org.apache.juneau.Context> value) { 159 super.type(value); 160 return this; 161 } 162 163 // </FluentSetters> 164 } 165 166 //------------------------------------------------------------------------------------------------------------------- 167 // Instance 168 //------------------------------------------------------------------------------------------------------------------- 169 170 @Override /* Context */ 171 public Builder copy() { 172 return new Builder(this); 173 } 174 175 private final ConcurrentHashMap<String,String> cache = new ConcurrentHashMap<>(); 176 177 /** 178 * Constructor. 179 * 180 * @param builder The builder for this object. 181 */ 182 public ClasspathStore(Builder builder) { 183 super(builder); 184 } 185 186 @Override /* ConfigStore */ 187 public synchronized String read(String name) throws IOException { 188 String s = cache.get(name); 189 if (s != null) 190 return s; 191 192 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 193 try (InputStream in = cl.getResourceAsStream(name)) { 194 if (in != null) 195 cache.put(name, IOUtils.read(in)); 196 } 197 return emptyIfNull(cache.get(name)); 198 } 199 200 @Override /* ConfigStore */ 201 public synchronized String write(String name, String expectedContents, String newContents) throws IOException { 202 203 // This is a no-op. 204 if (eq(expectedContents, newContents)) 205 return null; 206 207 String currentContents = read(name); 208 209 if (expectedContents != null && ! eq(currentContents, expectedContents)) 210 return currentContents; 211 212 update(name, newContents); 213 214 return null; 215 } 216 217 @Override /* ConfigStore */ 218 public synchronized boolean exists(String name) { 219 try { 220 return ! read(name).isEmpty(); 221 } catch (IOException e) { 222 return false; 223 } 224 } 225 226 @Override /* ConfigStore */ 227 public synchronized ClasspathStore update(String name, String newContents) { 228 if (newContents == null) 229 cache.remove(name); 230 else 231 cache.put(name, newContents); 232 super.update(name, newContents); 233 return this; 234 } 235 236 /** 237 * No-op. 238 */ 239 @Override /* Closeable */ 240 public void close() throws IOException { 241 // No-op 242 } 243}