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 * ----- Copy ----- 233 */ 234 235 /** 236 * A document id and its primary type and mixin types. 237 */ 238 public static final class IdWithTypes implements Serializable { 239 private static final long serialVersionUID = 1L; 240 241 public final Serializable id; 242 243 public final String primaryType; 244 245 public final String[] mixinTypes; 246 247 public IdWithTypes(Serializable id, String primaryType, String[] mixinTypes) { 248 this.id = id; 249 this.primaryType = primaryType; 250 this.mixinTypes = mixinTypes; 251 } 252 253 public IdWithTypes(Node node) { 254 this.id = node.getId(); 255 this.primaryType = node.getPrimaryType(); 256 this.mixinTypes = node.getMixinTypes(); 257 } 258 259 public IdWithTypes(SimpleFragment hierFragment) { 260 this.id = hierFragment.getId(); 261 this.primaryType = hierFragment.getString(Model.MAIN_PRIMARY_TYPE_KEY); 262 this.mixinTypes = (String[]) hierFragment.get(Model.MAIN_MIXIN_TYPES_KEY); 263 } 264 265 @Override 266 public String toString() { 267 return getClass().getSimpleName() + "(id=" + id + ",primaryType=" + primaryType + ",mixinTypes=" 268 + Arrays.toString(mixinTypes) + ")"; 269 } 270 } 271 272 public static final class CopyResult implements Serializable { 273 private static final long serialVersionUID = 1L; 274 275 /** The id of the root of the copy. */ 276 public final Serializable copyId; 277 278 /** The invalidations generated by the copy. */ 279 public final Invalidations invalidations; 280 281 /** The ids of newly created proxies. */ 282 public final Set<Serializable> proxyIds; 283 284 public CopyResult(Serializable copyId, Invalidations invalidations, Set<Serializable> proxyIds) { 285 this.copyId = copyId; 286 this.invalidations = invalidations; 287 this.proxyIds = proxyIds; 288 } 289 } 290 291 /** 292 * Copies the hierarchy starting from a given row to a new parent with a new name. 293 * <p> 294 * If the new parent is {@code null}, then this is a version creation, which doesn't recurse in regular children. 295 * <p> 296 * If {@code overwriteRow} is passed, the copy is done onto this existing node as its root (version restore) instead 297 * of creating a new node in the parent. 298 * 299 * @param source the id, primary type and mixin types of the row to copy 300 * @param destParentId the new parent id, or {@code null} 301 * @param destName the new name 302 * @param overwriteRow when not {@code null}, the copy is done onto this existing row, and the values are set in 303 * hierarchy 304 * @return info about the copy 305 */ 306 CopyResult copy(IdWithTypes source, Serializable destParentId, String destName, Row overwriteRow); 307 308 /** 309 * A document id, parent id and primary type, along with the version and proxy information (the potentially impacted 310 * selections). 311 * <p> 312 * Used to return info about a descendants tree for removal. 313 */ 314 public static final class NodeInfo implements Serializable { 315 private static final long serialVersionUID = 1L; 316 317 public final Serializable id; 318 319 public final Serializable parentId; 320 321 public final String primaryType; 322 323 public final Boolean isProperty; 324 325 public final Serializable versionSeriesId; 326 327 public final Serializable targetId; 328 329 /** 330 * Creates node info for a node that may also be a proxy. 331 */ 332 public NodeInfo(Serializable id, Serializable parentId, String primaryType, Boolean isProperty, 333 Serializable versionSeriesId, Serializable targetId) { 334 this.id = id; 335 this.parentId = parentId; 336 this.primaryType = primaryType; 337 this.isProperty = isProperty; 338 this.versionSeriesId = versionSeriesId; 339 this.targetId = targetId; 340 } 341 342 /** 343 * Creates node info for a node that may also be a proxy or a version. 344 */ 345 public NodeInfo(SimpleFragment hierFragment, SimpleFragment versionFragment, SimpleFragment proxyFragment) { 346 id = hierFragment.getId(); 347 parentId = hierFragment.get(Model.HIER_PARENT_KEY); 348 primaryType = hierFragment.getString(Model.MAIN_PRIMARY_TYPE_KEY); 349 isProperty = (Boolean) hierFragment.get(Model.HIER_CHILD_ISPROPERTY_KEY); 350 Serializable ps = proxyFragment == null ? null : proxyFragment.get(Model.PROXY_VERSIONABLE_KEY); 351 if (ps == null) { 352 versionSeriesId = versionFragment == null ? null : versionFragment.get(Model.VERSION_VERSIONABLE_KEY); 353 // may still be null 354 targetId = null; // marks it as a version if versionableId not 355 // null 356 } else { 357 versionSeriesId = ps; 358 targetId = proxyFragment.get(Model.PROXY_TARGET_KEY); 359 } 360 } 361 } 362 363 /** 364 * Deletes a hierarchy and returns information to generate invalidations. 365 * 366 * @param rootInfo info about the root to be deleted with its children (root id, and the rest is for invalidations) 367 * @return info about the descendants removed (including the root) 368 */ 369 List<NodeInfo> remove(NodeInfo rootInfo); 370 371 /** 372 * Processes and returns the invalidations queued for processing by the cache (if any). 373 * <p> 374 * Called pre-transaction by session start or transactionless save; 375 * 376 * @return the invalidations, or {@code null} 377 */ 378 Invalidations receiveInvalidations(); 379 380 /** 381 * Post-transaction invalidations notification. 382 * <p> 383 * Called post-transaction by session commit/rollback or transactionless save. 384 * 385 * @param invalidations the known invalidations to send to others, or {@code null} 386 */ 387 void sendInvalidations(Invalidations invalidations); 388 389 /** 390 * Clears the mapper's cache (if any) 391 * <p> 392 * Called after a rollback, or a manual clear through RepositoryStatus MBean. 393 */ 394 void clearCache(); 395 396 /** 397 * Evaluate the cached elements size 398 * 399 * @since 5.7.2 400 */ 401 long getCacheSize(); 402 403 /** 404 * Rollback the XA Resource. 405 * <p> 406 * This is in the {@link RowMapper} interface because on rollback the cache must be invalidated. 407 */ 408 void rollback(Xid xid) throws XAException; 409 410}