001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
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 *
016 * Contributors:
017 *     Florent Guillaume
018 */
019
020package org.nuxeo.ecm.core.storage.sql;
021
022import java.io.Serializable;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.HashSet;
026import java.util.LinkedList;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030
031import javax.transaction.xa.XAException;
032import javax.transaction.xa.Xid;
033
034/**
035 * A {@link RowMapper} maps {@link Row}s to and from the database.
036 * <p>
037 * These are the operations that can benefit from a cache.
038 *
039 * @see SoftRefCachingRowMapper
040 */
041public interface RowMapper {
042
043    /**
044     * Computes a new unique id.
045     *
046     * @return a new unique id
047     */
048    Serializable generateNewId();
049
050    /*
051     * ----- Batch -----
052     */
053
054    /**
055     * Reads a set of rows for the given {@link RowId}s.
056     * <p>
057     * For each requested row, either a {@link Row} is found and returned, or a {@link RowId} (not implementing
058     * {@link Row}) is returned to signify an absent row.
059     *
060     * @param rowIds the row ids (including their table name)
061     * @param cacheOnly if {@code true}, only hit memory
062     * @return the collection of {@link Row}s (or {@link RowId}s if the row was absent from the database). Order is not
063     *         the same as the input {@code rowIds}
064     */
065    List<? extends RowId> read(Collection<RowId> rowIds, boolean cacheOnly);
066
067    /**
068     * A {@link Row} and a list of its keys that have to be updated.
069     */
070    public static final class RowUpdate implements Serializable {
071        private static final long serialVersionUID = 1L;
072
073        public final Row row;
074
075        // used for simple fragments
076        public final Collection<String> keys;
077
078        // used for collection fragment right push, the pos at which to start to insert
079        // if -1 then a full update must be done
080        public final int pos;
081
082        // conditions to add to get a conditional update, for change token
083        public Map<String, Serializable> conditions;
084
085        /** Constructor for simple fragment update. */
086        public RowUpdate(Row row, Collection<String> keys) {
087            this.row = row;
088            this.keys = keys;
089            pos = -1;
090        }
091
092        /** Constructor for collection fragment full update. */
093        public RowUpdate(Row row) {
094            this(row, -1);
095        }
096
097        /** Constructor for collection fragment right push update. */
098        public RowUpdate(Row row, int pos) {
099            this.row = row;
100            keys = null;
101            this.pos = pos;
102        }
103
104        public void setConditions(Map<String, Serializable> conditions) {
105            this.conditions = conditions;
106        }
107
108        @Override
109        public int hashCode() {
110            return row.hashCode();
111        }
112
113        @Override
114        public boolean equals(Object other) {
115            if (other instanceof RowUpdate) {
116                return equal((RowUpdate) other);
117            }
118            return false;
119        }
120
121        private boolean equal(RowUpdate other) {
122            return other.row.equals(row);
123        }
124
125        @Override
126        public String toString() {
127            String string = getClass().getSimpleName() + '(' + row + ", keys=" + keys + ')';
128            if (conditions != null && !conditions.isEmpty()) {
129                string += "(IF=" + conditions + ')';
130            }
131            return string;
132        }
133    }
134
135    /**
136     * The description of a set of rows to create, update or delete.
137     */
138    public static class RowBatch implements Serializable {
139        private static final long serialVersionUID = 1L;
140
141        /**
142         * Creates are done first and are ordered.
143         */
144        public final List<Row> creates;
145
146        /**
147         * Updates.
148         */
149        public final Set<RowUpdate> updates;
150
151        /**
152         * Deletes are done last.
153         */
154        public final Set<RowId> deletes;
155
156        /**
157         * Dependent deletes aren't executed in the database but still trigger invalidations.
158         */
159        public final Set<RowId> deletesDependent;
160
161        public RowBatch() {
162            creates = new LinkedList<Row>();
163            updates = new HashSet<RowUpdate>();
164            deletes = new HashSet<RowId>();
165            deletesDependent = new HashSet<RowId>();
166        }
167
168        public boolean isEmpty() {
169            return creates.isEmpty() && updates.isEmpty() && deletes.isEmpty() && deletesDependent.isEmpty();
170        }
171
172        @Override
173        public String toString() {
174            return getClass().getSimpleName() + "(creates=" + creates + ", updates=" + updates + ", deletes=" + deletes
175                    + ", deletesDependent=" + deletesDependent + ')';
176        }
177    }
178
179    /**
180     * Writes a set of rows. This includes creating, updating and deleting rows.
181     *
182     * @param batch the set of rows and the operations to do on them
183     */
184    void write(RowBatch batch);
185
186    /*
187     * ----- Read -----
188     */
189
190    /**
191     * Gets a row for a {@link SimpleFragment} from the database, given its table name and id. If the row doesn't exist,
192     * {@code null} is returned.
193     *
194     * @param rowId the row id
195     * @return the row, or {@code null}
196     */
197    Row readSimpleRow(RowId rowId);
198
199    /**
200     * Gets the fulltext extracted from the binary fields.
201     *
202     * @since 5.9.3
203     * @param rowId the row id
204     * @return the fulltext string representation or {@code null} if unsupported
205     */
206    Map<String, String> getBinaryFulltext(RowId rowId);
207
208    /**
209     * Gets an array for a {@link CollectionFragment} from the database, given its table name and id. If no rows are
210     * found, an empty array is returned.
211     *
212     * @param rowId the row id
213     * @return the array
214     */
215    Serializable[] readCollectionRowArray(RowId rowId);
216
217    /**
218     * Reads the rows corresponding to a selection.
219     *
220     * @param selType the selection type
221     * @param selId the selection id (parent id for a hierarchy selection)
222     * @param filter the filter value (name for a hierarchy selection)
223     * @param criterion an optional additional criterion depending on the selection type (complex prop flag for a
224     *            hierarchy selection)
225     * @param limitToOne whether to stop after one row retrieved
226     * @return the list of rows
227     */
228    List<Row> readSelectionRows(SelectionType selType, Serializable selId, Serializable filter, Serializable criterion,
229            boolean limitToOne);
230
231    /**
232     * Gets all the selection ids for a given list of values.
233     *
234     * @since 9.2
235     */
236    Set<Serializable> readSelectionsIds(SelectionType selType, List<Serializable> values);
237
238    /*
239     * ----- Copy -----
240     */
241
242    /**
243     * A document id and its primary type and mixin types.
244     */
245    public static final class IdWithTypes implements Serializable {
246        private static final long serialVersionUID = 1L;
247
248        public final Serializable id;
249
250        public final String primaryType;
251
252        public final String[] mixinTypes;
253
254        public IdWithTypes(Serializable id, String primaryType, String[] mixinTypes) {
255            this.id = id;
256            this.primaryType = primaryType;
257            this.mixinTypes = mixinTypes;
258        }
259
260        public IdWithTypes(Node node) {
261            this.id = node.getId();
262            this.primaryType = node.getPrimaryType();
263            this.mixinTypes = node.getMixinTypes();
264        }
265
266        public IdWithTypes(SimpleFragment hierFragment) {
267            this.id = hierFragment.getId();
268            this.primaryType = hierFragment.getString(Model.MAIN_PRIMARY_TYPE_KEY);
269            this.mixinTypes = (String[]) hierFragment.get(Model.MAIN_MIXIN_TYPES_KEY);
270        }
271
272        @Override
273        public String toString() {
274            return getClass().getSimpleName() + "(id=" + id + ",primaryType=" + primaryType + ",mixinTypes="
275                    + Arrays.toString(mixinTypes) + ")";
276        }
277    }
278
279    public static final class CopyResult implements Serializable {
280        private static final long serialVersionUID = 1L;
281
282        /** The id of the root of the copy. */
283        public final Serializable copyId;
284
285        /** The invalidations generated by the copy. */
286        public final Invalidations invalidations;
287
288        /** The ids of newly created proxies. */
289        public final Set<Serializable> proxyIds;
290
291        public CopyResult(Serializable copyId, Invalidations invalidations, Set<Serializable> proxyIds) {
292            this.copyId = copyId;
293            this.invalidations = invalidations;
294            this.proxyIds = proxyIds;
295        }
296    }
297
298    /**
299     * Copies the hierarchy starting from a given row to a new parent with a new name.
300     * <p>
301     * If the new parent is {@code null}, then this is a version creation, which doesn't recurse in regular children.
302     * <p>
303     * If {@code overwriteRow} is passed, the copy is done onto this existing node as its root (version restore) instead
304     * of creating a new node in the parent.
305     *
306     * @param source the id, primary type and mixin types of the row to copy
307     * @param destParentId the new parent id, or {@code null}
308     * @param destName the new name
309     * @param overwriteRow when not {@code null}, the copy is done onto this existing row, and the values are set in
310     *            hierarchy
311     * @return info about the copy
312     */
313    CopyResult copy(IdWithTypes source, Serializable destParentId, String destName, Row overwriteRow);
314
315    /**
316     * A document id, parent id and primary type, along with the version and proxy information (the potentially impacted
317     * selections).
318     * <p>
319     * Used to return info about a descendants tree for removal.
320     */
321    public static final class NodeInfo implements Serializable {
322        private static final long serialVersionUID = 1L;
323
324        public final Serializable id;
325
326        public final Serializable parentId;
327
328        public final String primaryType;
329
330        public final Boolean isProperty;
331
332        public final Serializable versionSeriesId;
333
334        public final Serializable targetId;
335
336        /**
337         * Creates node info for a node that may also be a proxy.
338         */
339        public NodeInfo(Serializable id, Serializable parentId, String primaryType, Boolean isProperty,
340                Serializable versionSeriesId, Serializable targetId) {
341            this.id = id;
342            this.parentId = parentId;
343            this.primaryType = primaryType;
344            this.isProperty = isProperty;
345            this.versionSeriesId = versionSeriesId;
346            this.targetId = targetId;
347        }
348
349        /**
350         * Creates node info for a node that may also be a proxy or a version.
351         */
352        public NodeInfo(SimpleFragment hierFragment, SimpleFragment versionFragment, SimpleFragment proxyFragment) {
353            id = hierFragment.getId();
354            parentId = hierFragment.get(Model.HIER_PARENT_KEY);
355            primaryType = hierFragment.getString(Model.MAIN_PRIMARY_TYPE_KEY);
356            isProperty = (Boolean) hierFragment.get(Model.HIER_CHILD_ISPROPERTY_KEY);
357            Serializable ps = proxyFragment == null ? null : proxyFragment.get(Model.PROXY_VERSIONABLE_KEY);
358            if (ps == null) {
359                versionSeriesId = versionFragment == null ? null : versionFragment.get(Model.VERSION_VERSIONABLE_KEY);
360                // may still be null
361                targetId = null; // marks it as a version if versionableId not
362                                 // null
363            } else {
364                versionSeriesId = ps;
365                targetId = proxyFragment.get(Model.PROXY_TARGET_KEY);
366            }
367        }
368    }
369
370    /**
371     * Gets descendants infos from a given root node. This does not include information about the root node itself.
372     *
373     * @param rootId the root node id from which to get descendants info
374     * @return the list of descendant nodes info
375     * @since 9.2
376     */
377    List<NodeInfo> getDescendantsInfo(Serializable rootId);
378
379    /**
380     * Deletes a hierarchy.
381     *
382     * @param rootId the id of the root node to be deleted with its children
383     * @param nodeInfos the information about all descendants being deleted along the root node
384     * @since 9.2
385     */
386    void remove(Serializable rootId, List<NodeInfo> nodeInfos);
387
388    /**
389     * Processes and returns the invalidations queued for processing by the cache (if any).
390     * <p>
391     * Called pre-transaction by session start or transactionless save;
392     *
393     * @return the invalidations, or {@code null}
394     */
395    Invalidations receiveInvalidations();
396
397    /**
398     * Post-transaction invalidations notification.
399     * <p>
400     * Called post-transaction by session commit/rollback or transactionless save.
401     *
402     * @param invalidations the known invalidations to send to others, or {@code null}
403     */
404    void sendInvalidations(Invalidations invalidations);
405
406    /**
407     * Clears the mapper's cache (if any)
408     * <p>
409     * Called after a rollback, or a manual clear through RepositoryStatus MBean.
410     */
411    void clearCache();
412
413    /**
414     * Evaluate the cached elements size
415     *
416     * @since 5.7.2
417     */
418    long getCacheSize();
419
420    /**
421     * Rollback the XA Resource.
422     * <p>
423     * This is in the {@link RowMapper} interface because on rollback the cache must be invalidated.
424     */
425    void rollback(Xid xid) throws XAException;
426
427}