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