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.internal.StringUtils.*; 016 017import java.io.*; 018import java.util.concurrent.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.annotation.*; 022import org.apache.juneau.internal.*; 023 024/** 025 * Classpath-based storage location for configuration files. 026 * 027 * <p> 028 * Looks inside the JVM classpath for configuration files. 029 * 030 * <p> 031 * Configuration files retrieved from the classpath can be modified but not persisted. 032 */ 033@ConfigurableContext 034public class ConfigClasspathStore extends ConfigStore { 035 036 //------------------------------------------------------------------------------------------------------------------- 037 // Configurable properties 038 //------------------------------------------------------------------------------------------------------------------- 039 040 static final String PREFIX = "ConfigClasspathStore"; 041 042 //------------------------------------------------------------------------------------------------------------------- 043 // Predefined instances 044 //------------------------------------------------------------------------------------------------------------------- 045 046 /** Default memory store, all default values.*/ 047 public static final ConfigClasspathStore DEFAULT = ConfigClasspathStore.create().build(); 048 049 050 //------------------------------------------------------------------------------------------------------------------- 051 // Instance 052 //------------------------------------------------------------------------------------------------------------------- 053 054 /** 055 * Create a new builder for this object. 056 * 057 * @return A new builder for this object. 058 */ 059 public static ConfigClasspathStoreBuilder create() { 060 return new ConfigClasspathStoreBuilder(); 061 } 062 063 @Override /* Context */ 064 public ConfigClasspathStoreBuilder builder() { 065 return new ConfigClasspathStoreBuilder(getPropertyStore()); 066 } 067 068 private final ConcurrentHashMap<String,String> cache = new ConcurrentHashMap<>(); 069 070 /** 071 * Constructor. 072 * 073 * @param ps The settings for this content store. 074 */ 075 protected ConfigClasspathStore(PropertyStore ps) { 076 super(ps); 077 } 078 079 @Override /* ConfigStore */ 080 public synchronized String read(String name) throws IOException { 081 String s = cache.get(name); 082 if (s != null) 083 return s; 084 085 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 086 try (InputStream in = cl.getResourceAsStream(name)) { 087 if (in != null) 088 cache.put(name, IOUtils.read(in, IOUtils.UTF8)); 089 } 090 return emptyIfNull(cache.get(name)); 091 } 092 093 @Override /* ConfigStore */ 094 public synchronized String write(String name, String expectedContents, String newContents) throws IOException { 095 096 // This is a no-op. 097 if (isEquals(expectedContents, newContents)) 098 return null; 099 100 String currentContents = read(name); 101 102 if (expectedContents != null && ! isEquals(currentContents, expectedContents)) 103 return currentContents; 104 105 update(name, newContents); 106 107 return null; 108 } 109 110 @Override /* ConfigStore */ 111 public synchronized boolean exists(String name) { 112 try { 113 return ! read(name).isEmpty(); 114 } catch (IOException e) { 115 return false; 116 } 117 } 118 119 @Override /* ConfigStore */ 120 public synchronized ConfigClasspathStore update(String name, String newContents) { 121 if (newContents == null) 122 cache.remove(name); 123 else 124 cache.put(name, newContents); 125 super.update(name, newContents); 126 return this; 127 } 128 129 /** 130 * No-op. 131 */ 132 @Override /* Closeable */ 133 public void close() throws IOException { 134 // No-op 135 } 136 137 //----------------------------------------------------------------------------------------------------------------- 138 // Other methods. 139 //----------------------------------------------------------------------------------------------------------------- 140 141 @Override /* Context */ 142 public ObjectMap toMap() { 143 return super.toMap() 144 .append("ConfigClasspathStore", new DefaultFilteringObjectMap() 145 ); 146 } 147}