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