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.examples.core.config.store; 014 015import java.util.*; 016import java.util.concurrent.*; 017 018import org.apache.juneau.common.internal.*; 019import org.apache.juneau.config.store.*; 020 021/** 022 * Example of a {@link ConfigStore} that uses a relational database as a backend. 023 * 024 * <p> 025 * This class provides a basic framework but requires implementing the following methods: 026 * <ul class='javatree'> 027 * <li class='jm'>{@link #getDatabaseValue(String)} 028 * <li class='jm'>{@link #exists(String)} 029 * </ul> 030 * 031 * <h5 class='section'>See Also:</h5><ul> 032 * </ul> 033 */ 034@SuppressWarnings({"resource","unused","javadoc"}) 035public class SqlStore extends ConfigStore { 036 037 //----------------------------------------------------------------------------------------------------------------- 038 // Static 039 //----------------------------------------------------------------------------------------------------------------- 040 041 static final String 042 SQLSTORE_jdbcUrl = "SqlStore.jdbcUrl", 043 SQLSTORE_tableName = "SqlStore.tableName", 044 SQLSTORE_nameColumn = "SqlStore.nameColumn", 045 SQLSTORE_valueColumn = "SqlStore.valueColumn", 046 SQLSTORE_pollInterval = "SqlStore.pollInterval"; 047 048 049 /** 050 * Instantiates a builder for this object. 051 * 052 * @return A new builder for this object. 053 */ 054 public static Builder create() { 055 return new Builder(); 056 } 057 058 //----------------------------------------------------------------------------------------------------------------- 059 // Builder 060 //----------------------------------------------------------------------------------------------------------------- 061 062 /** 063 * Builder class. 064 */ 065 public static class Builder extends ConfigStore.Builder { 066 067 String jdbcUrl, tableName, nameColumn, valueColumn; 068 int pollInterval; 069 070 Builder() { 071 this.jdbcUrl = env(SQLSTORE_jdbcUrl, "jdbc:derby:mydb"); 072 this.tableName = env(SQLSTORE_tableName, "config"); 073 this.nameColumn = env(SQLSTORE_nameColumn, "name"); 074 this.valueColumn = env(SQLSTORE_valueColumn, "value"); 075 this.pollInterval = env(SQLSTORE_pollInterval, 600); // Time in seconds. 076 } 077 078 public Builder jdbcUrl(String value) { 079 this.jdbcUrl = value; 080 return this; 081 } 082 083 public Builder tableName(String value) { 084 this.tableName = value; 085 return this; 086 } 087 088 public Builder nameColumn(String value) { 089 this.nameColumn = value; 090 return this; 091 } 092 093 public Builder valueColumn(String value) { 094 this.valueColumn = value; 095 return this; 096 } 097 098 public Builder pollInterval(int value) { 099 this.pollInterval = value; 100 return this; 101 } 102 103 @Override 104 public Builder copy() { 105 return null; 106 } 107 108 @Override 109 public SqlStore build() { 110 return build(SqlStore.class); 111 } 112 } 113 114 //----------------------------------------------------------------------------------------------------------------- 115 // Instance 116 //----------------------------------------------------------------------------------------------------------------- 117 118 private final String jdbcUrl; 119 private final String tableName, nameColumn, valueColumn; 120 private final Timer watcher; 121 private final ConcurrentHashMap<String,String> cache = new ConcurrentHashMap<>(); 122 123 /** 124 * Constructor. 125 * 126 * @param builder The builder for this object. 127 */ 128 protected SqlStore(Builder builder) { 129 super(builder); 130 this.jdbcUrl = builder.jdbcUrl; 131 this.tableName = builder.tableName; 132 this.nameColumn = builder.nameColumn; 133 this.valueColumn = builder.valueColumn; 134 135 int pollInterval = builder.pollInterval; 136 137 TimerTask timerTask = new TimerTask() { 138 @Override 139 public void run() { 140 SqlStore.this.poll(); 141 } 142 }; 143 144 this.watcher = new Timer("MyTimer"); 145 watcher.scheduleAtFixedRate(timerTask, 0, pollInterval * 1000); 146 } 147 148 synchronized void poll() { 149 150 // Loop through all our entries and find the latest values. 151 cache.forEach((name,cacheContents) -> { 152 String newContents = getDatabaseValue(name); 153 154 // Change detected! 155 if (! cacheContents.equals(newContents)) 156 update(name, newContents); 157 }); 158 } 159 160 // Reads the value from the database. 161 protected String getDatabaseValue(String name) { 162 // Implement me! 163 return null; 164 } 165 166 @Override /* ConfigStore */ 167 public boolean exists(String name) { 168 // Implement me! 169 return false; 170 } 171 172 @Override /* ConfigStore */ 173 public synchronized String read(String name) { 174 String contents = cache.get(name); 175 if (contents == null) { 176 contents = getDatabaseValue(name); 177 update(name, contents); 178 } 179 return contents; 180 } 181 182 @Override /* ConfigStore */ 183 public synchronized String write(String name, String expectedContents, String newContents) { 184 185 // This is a no-op. 186 if (StringUtils.eq(expectedContents, newContents)) 187 return null; 188 189 String currentContents = read(name); 190 191 if (expectedContents != null && StringUtils.ne(currentContents, expectedContents)) 192 return currentContents; 193 194 update(name, newContents); 195 196 // Success! 197 return null; 198 } 199 200 @Override /* ConfigStore */ 201 public synchronized SqlStore update(String name, String newContents) { 202 cache.put(name, newContents); 203 super.update(name, newContents); // Trigger any listeners. 204 return this; 205 } 206 207 @Override /* Closeable */ 208 public synchronized void close() { 209 if (watcher != null) 210 watcher.cancel(); 211 } 212}