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