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 super(); 073 } 074 075 /** 076 * Copy constructor. 077 * 078 * @param copyFrom The bean to copy from. 079 */ 080 protected Builder(ClasspathStore copyFrom) { 081 super(copyFrom); 082 type(copyFrom.getClass()); 083 } 084 085 /** 086 * Copy constructor. 087 * 088 * @param copyFrom The builder to copy from. 089 */ 090 protected Builder(Builder copyFrom) { 091 super(copyFrom); 092 } 093 094 @Override /* Context.Builder */ 095 public Builder copy() { 096 return new Builder(this); 097 } 098 099 @Override /* Context.Builder */ 100 public ClasspathStore build() { 101 return build(ClasspathStore.class); 102 } 103 104 //----------------------------------------------------------------------------------------------------------------- 105 // Properties 106 //----------------------------------------------------------------------------------------------------------------- 107 108 // <FluentSetters> 109 110 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 111 public Builder annotations(Annotation...values) { 112 super.annotations(values); 113 return this; 114 } 115 116 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 117 public Builder apply(AnnotationWorkList work) { 118 super.apply(work); 119 return this; 120 } 121 122 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 123 public Builder applyAnnotations(java.lang.Class<?>...fromClasses) { 124 super.applyAnnotations(fromClasses); 125 return this; 126 } 127 128 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 129 public Builder applyAnnotations(Method...fromMethods) { 130 super.applyAnnotations(fromMethods); 131 return this; 132 } 133 134 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 135 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 136 super.cache(value); 137 return this; 138 } 139 140 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 141 public Builder debug() { 142 super.debug(); 143 return this; 144 } 145 146 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 147 public Builder debug(boolean value) { 148 super.debug(value); 149 return this; 150 } 151 152 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 153 public Builder impl(Context value) { 154 super.impl(value); 155 return this; 156 } 157 158 @Override /* GENERATED - org.apache.juneau.Context.Builder */ 159 public Builder type(Class<? extends org.apache.juneau.Context> value) { 160 super.type(value); 161 return this; 162 } 163 164 // </FluentSetters> 165 } 166 167 //------------------------------------------------------------------------------------------------------------------- 168 // Instance 169 //------------------------------------------------------------------------------------------------------------------- 170 171 @Override /* Context */ 172 public Builder copy() { 173 return new Builder(this); 174 } 175 176 private final ConcurrentHashMap<String,String> cache = new ConcurrentHashMap<>(); 177 178 /** 179 * Constructor. 180 * 181 * @param builder The builder for this object. 182 */ 183 public ClasspathStore(Builder builder) { 184 super(builder); 185 } 186 187 @Override /* ConfigStore */ 188 public synchronized String read(String name) throws IOException { 189 String s = cache.get(name); 190 if (s != null) 191 return s; 192 193 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 194 try (InputStream in = cl.getResourceAsStream(name)) { 195 if (in != null) 196 cache.put(name, IOUtils.read(in)); 197 } 198 return emptyIfNull(cache.get(name)); 199 } 200 201 @Override /* ConfigStore */ 202 public synchronized String write(String name, String expectedContents, String newContents) throws IOException { 203 204 // This is a no-op. 205 if (eq(expectedContents, newContents)) 206 return null; 207 208 String currentContents = read(name); 209 210 if (expectedContents != null && ! eq(currentContents, expectedContents)) 211 return currentContents; 212 213 update(name, newContents); 214 215 return null; 216 } 217 218 @Override /* ConfigStore */ 219 public synchronized boolean exists(String name) { 220 try { 221 return ! read(name).isEmpty(); 222 } catch (IOException e) { 223 return false; 224 } 225 } 226 227 @Override /* ConfigStore */ 228 public synchronized ClasspathStore update(String name, String newContents) { 229 if (newContents == null) 230 cache.remove(name); 231 else 232 cache.put(name, newContents); 233 super.update(name, newContents); 234 return this; 235 } 236 237 /** 238 * No-op. 239 */ 240 @Override /* Closeable */ 241 public void close() throws IOException { 242 // No-op 243 } 244}