001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.schematic;
017
018import java.util.Collection;
019import java.util.List;
020import org.modeshape.schematic.annotation.RequiresTransaction;
021import org.modeshape.schematic.document.Document;
022import org.modeshape.schematic.document.EditableDocument;
023
024/**
025 * A store for JSON documents and other binary content, plus a library of JSON Schema documents used to describe and validate the
026 * stored documents.
027 * 
028 * @author Horia Chiorean <hchiorea@redhat.com>
029 * @since 5.0
030 */
031public interface SchematicDb extends TransactionListener, Lifecycle, Lockable {
032
033    /**
034     * Returns a unique identifier for this schematic DB. 
035     * <p>
036     * Implementations should make sure that the provided id  is unique per storage instance. 
037     * In other words, if two {@link SchematicDb} instances of the same type store data in two different places, they should
038     * return a different id.
039     * </p>
040     * 
041     * @return a {@link String}, never {@code null}
042     */
043    String id();
044    
045    /**
046     * Returns a set over all the keys present in the DB.
047     * <p>
048     * If this method is called within an existing transaction, it should take into account the transient transactional context
049     * (i.e. any local but not yet committed changes)
050     * </p>
051     * 
052     * @return a {@link List} instance, never {@code null}
053     */
054    List<String> keys();
055    
056    /**
057     * Get the document with the supplied key. This will represent the full {@link SchematicEntry} document if one exists. 
058     * <p>
059     * If this method is called within an existing transaction, it should take into account the transient transactional context
060     * (i.e. any local but not yet committed changes)
061     * </p>
062     *
063     * @param key the key or identifier for the document
064     * @return the document, or null if there was no document with the supplied key
065     */
066    Document get( String key );
067
068    /**
069     * Loads a set of documents from the DB returning the corresponding schematic entries.
070     * 
071     * <p>
072     * If this method is called within an existing transaction, it should <b>take into account</b> the transient transactional 
073     * context (i.e. any local but not yet committed changes) and either use that (if it exists) or the persisted information.
074     * </p>
075     * 
076     * @param keys an {@link Collection} of keys; never {@code null}
077     * @return a {@link List} of {@link SchematicEntry entries}; never {@code null} 
078     */
079    List<SchematicEntry> load(Collection<String> keys);
080    
081    /**
082     * Stores the supplied schematic entry under the given key. If an entry already exists with the same key, it should be
083     * overwritten.
084     * @param key a schematic entry id, never {@code null}
085     * @param entry a {@link SchematicEntry} instance, never {@code null}
086     */
087    @RequiresTransaction
088    void put(String key, SchematicEntry entry);
089
090    /**
091     * Get an editor for the content of the given entry with the supplied key. 
092     *
093     * @param key the key or identifier for the document
094     * @param createIfMissing true if a new entry should be created and added to the database if an existing entry does not exist
095     * @return the content document, or null if there was no document with the supplied key and a new one could not be created
096     */
097    @RequiresTransaction
098    EditableDocument editContent(String key, boolean createIfMissing);
099
100    /**
101     * Store the supplied content at the given key.
102     * 
103     * <p>
104     *     Depending on the actual implementation, this may or may not be thread-safe. ModeShape never assumes this is thread-safe
105     *     when calling it.
106     * </p>
107     *
108     * @param key the key or identifier for the content
109     * @param content the content that is to be stored
110     * @return the existing entry for the supplied key, or null if there was no entry and the put was successful
111     */
112    @RequiresTransaction
113    SchematicEntry putIfAbsent(String key, Document content);
114
115    /**
116     * Remove the existing document at the given key.
117     *
118     * @param key the key or identifier for the document
119     * @return {@code true} if the removal was successful, {@code false} otherwise
120     */
121    @RequiresTransaction
122    boolean remove(String key);
123
124    /**
125     * Removes all the entries from this DB.
126     */
127    @RequiresTransaction
128    void removeAll();
129
130    /**
131     * Store the supplied content document at the given key. If a document already exists with the given key, this should
132     * overwrite the existing document.
133     *
134     * @param key the key or identifier for the document
135     * @param content the document that is to be stored
136     * @see #putIfAbsent(String, Document)
137     */
138    @RequiresTransaction
139    default void put( String key, Document content ) {
140        put(key, SchematicEntry.create(key, content));
141    }
142
143    @Override
144    @RequiresTransaction
145    default boolean lockForWriting( List<String> locks ) {
146        throw new UnsupportedOperationException(getClass() +  " does not support exclusive locking");
147    }
148
149    /**
150     * Get the entry with the supplied key.
151     * <p>
152     * If this method is called within an existing transaction, it should take into account the transient transactional context
153     * (i.e. any local but not yet committed changes)
154     * </p>
155     *
156     * @param key the key or identifier for the document
157     * @return the entry, or null if there was no document with the supplied key
158     */
159    default SchematicEntry getEntry( String key) {
160        Document doc = get(key);
161        return doc != null ? () -> doc : null;    
162    }
163    
164    /**
165     * Determine whether the database contains an entry with the supplied key.
166     * <p>
167     * If this method is called within an existing transaction, it should take into account the transient transactional context
168     * (i.e. any local but not yet committed changes)
169     * </p>
170     *
171     * @param key the key or identifier for the document
172     * @return true if the database contains an entry with this key, or false otherwise
173     */
174    default boolean containsKey( String key ) {
175        return get(key) != null;
176    }
177
178    /**
179     * Store the supplied document. This document is expected to be a full entry document, which contains both a "metadata"
180     * and "content" section.
181     * 
182     * @param entryDocument the document that contains the metadata document and content document.
183     * @see #putIfAbsent(String, Document)
184     */
185    @RequiresTransaction
186    default void putEntry(Document entryDocument) {
187        SchematicEntry entry = SchematicEntry.fromDocument(entryDocument);
188        put(entry.id(), entry);
189    }
190}