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}