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 /** Constructor for simple fragment update. */ 083 public RowUpdate(Row row, Collection<String> keys) { 084 this.row = row; 085 this.keys = keys; 086 pos = -1; 087 } 088 089 /** Constructor for collection fragment full update. */ 090 public RowUpdate(Row row) { 091 this(row, -1); 092 } 093 094 /** Constructor for collection fragment right push update. */ 095 public RowUpdate(Row row, int pos) { 096 this.row = row; 097 keys = null; 098 this.pos = pos; 099 } 100 101 @Override 102 public int hashCode() { 103 return row.hashCode(); 104 } 105 106 @Override 107 public boolean equals(Object other) { 108 if (other instanceof RowUpdate) { 109 return equal((RowUpdate) other); 110 } 111 return false; 112 } 113 114 private boolean equal(RowUpdate other) { 115 return other.row.equals(row); 116 } 117 118 @Override 119 public String toString() { 120 return getClass().getSimpleName() + '(' + row + ", keys=" + keys + ')'; 121 } 122 } 123 124 /** 125 * The description of a set of rows to create, update or delete. 126 */ 127 public static class RowBatch implements Serializable { 128 private static final long serialVersionUID = 1L; 129 130 /** 131 * Creates are done first and are ordered. 132 */ 133 public final List<Row> creates; 134 135 /** 136 * Updates. 137 */ 138 public final Set<RowUpdate> updates; 139 140 /** 141 * Deletes are done last. 142 */ 143 public final Set<RowId> deletes; 144 145 /** 146 * Dependent deletes aren't executed in the database but still trigger invalidations. 147 */ 148 public final Set<RowId> deletesDependent; 149 150 public RowBatch() { 151 creates = new LinkedList<Row>(); 152 updates = new HashSet<RowUpdate>(); 153 deletes = new HashSet<RowId>(); 154 deletesDependent = new HashSet<RowId>(); 155 } 156 157 public boolean isEmpty() { 158 return creates.isEmpty() && updates.isEmpty() && deletes.isEmpty() && deletesDependent.isEmpty(); 159 } 160 161 @Override 162 public String toString() { 163 return getClass().getSimpleName() + "(creates=" + creates + ", updates=" + updates + ", deletes=" + deletes 164 + ", deletesDependent=" + deletesDependent + ')'; 165 } 166 } 167 168 /** 169 * Writes a set of rows. This includes creating, updating and deleting rows. 170 * 171 * @param batch the set of rows and the operations to do on them 172 */ 173 void write(RowBatch batch); 174 175 /* 176 * ----- Read ----- 177 */ 178 179 /** 180 * Gets a row for a {@link SimpleFragment} from the database, given its table name and id. If the row doesn't exist, 181 * {@code null} is returned. 182 * 183 * @param rowId the row id 184 * @return the row, or {@code null} 185 */ 186 Row readSimpleRow(RowId rowId); 187 188 /** 189 * Gets the fulltext extracted from the binary fields. 190 * 191 * @since 5.9.3 192 * @param rowId the row id 193 * @return the fulltext string representation or {@code null} if unsupported 194 */ 195 Map<String, String> getBinaryFulltext(RowId rowId); 196 197 /** 198 * Gets an array for a {@link CollectionFragment} from the database, given its table name and id. If no rows are 199 * found, an empty array is returned. 200 * 201 * @param rowId the row id 202 * @return the array 203 */ 204 Serializable[] readCollectionRowArray(RowId rowId); 205 206 /** 207 * Reads the rows corresponding to a selection. 208 * 209 * @param selType the selection type 210 * @param selId the selection id (parent id for a hierarchy selection) 211 * @param filter the filter value (name for a hierarchy selection) 212 * @param criterion an optional additional criterion depending on the selection type (complex prop flag for a 213 * hierarchy selection) 214 * @param limitToOne whether to stop after one row retrieved 215 * @return the list of rows 216 */ 217 List<Row> readSelectionRows(SelectionType selType, Serializable selId, Serializable filter, Serializable criterion, 218 boolean limitToOne); 219 220 /* 221 * ----- Copy ----- 222 */ 223 224 /** 225 * A document id and its primary type and mixin types. 226 */ 227 public static final class IdWithTypes implements Serializable { 228 private static final long serialVersionUID = 1L; 229 230 public final Serializable id; 231 232 public final String primaryType; 233 234 public final String[] mixinTypes; 235 236 public IdWithTypes(Serializable id, String primaryType, String[] mixinTypes) { 237 this.id = id; 238 this.primaryType = primaryType; 239 this.mixinTypes = mixinTypes; 240 } 241 242 public IdWithTypes(Node node) { 243 this.id = node.getId(); 244 this.primaryType = node.getPrimaryType(); 245 this.mixinTypes = node.getMixinTypes(); 246 } 247 248 public IdWithTypes(SimpleFragment hierFragment) { 249 this.id = hierFragment.getId(); 250 this.primaryType = hierFragment.getString(Model.MAIN_PRIMARY_TYPE_KEY); 251 this.mixinTypes = (String[]) hierFragment.get(Model.MAIN_MIXIN_TYPES_KEY); 252 } 253 254 @Override 255 public String toString() { 256 return getClass().getSimpleName() + "(id=" + id + ",primaryType=" + primaryType + ",mixinTypes=" 257 + Arrays.toString(mixinTypes) + ")"; 258 } 259 } 260 261 public static final class CopyResult implements Serializable { 262 private static final long serialVersionUID = 1L; 263 264 /** The id of the root of the copy. */ 265 public final Serializable copyId; 266 267 /** The invalidations generated by the copy. */ 268 public final Invalidations invalidations; 269 270 /** The ids of newly created proxies. */ 271 public final Set<Serializable> proxyIds; 272 273 public CopyResult(Serializable copyId, Invalidations invalidations, Set<Serializable> proxyIds) { 274 this.copyId = copyId; 275 this.invalidations = invalidations; 276 this.proxyIds = proxyIds; 277 } 278 } 279 280 /** 281 * Copies the hierarchy starting from a given row to a new parent with a new name. 282 * <p> 283 * If the new parent is {@code null}, then this is a version creation, which doesn't recurse in regular children. 284 * <p> 285 * If {@code overwriteRow} is passed, the copy is done onto this existing node as its root (version restore) instead 286 * of creating a new node in the parent. 287 * 288 * @param source the id, primary type and mixin types of the row to copy 289 * @param destParentId the new parent id, or {@code null} 290 * @param destName the new name 291 * @param overwriteRow when not {@code null}, the copy is done onto this existing row, and the values are set in 292 * hierarchy 293 * @return info about the copy 294 */ 295 CopyResult copy(IdWithTypes source, Serializable destParentId, String destName, Row overwriteRow); 296 297 /** 298 * A document id, parent id and primary type, along with the version and proxy information (the potentially impacted 299 * selections). 300 * <p> 301 * Used to return info about a descendants tree for removal. 302 */ 303 public static final class NodeInfo implements Serializable { 304 private static final long serialVersionUID = 1L; 305 306 public final Serializable id; 307 308 public final Serializable parentId; 309 310 public final String primaryType; 311 312 public final Boolean isProperty; 313 314 public final Serializable versionSeriesId; 315 316 public final Serializable targetId; 317 318 /** 319 * Creates node info for a node that may also be a proxy. 320 */ 321 public NodeInfo(Serializable id, Serializable parentId, String primaryType, Boolean isProperty, 322 Serializable versionSeriesId, Serializable targetId) { 323 this.id = id; 324 this.parentId = parentId; 325 this.primaryType = primaryType; 326 this.isProperty = isProperty; 327 this.versionSeriesId = versionSeriesId; 328 this.targetId = targetId; 329 } 330 331 /** 332 * Creates node info for a node that may also be a proxy or a version. 333 */ 334 public NodeInfo(SimpleFragment hierFragment, SimpleFragment versionFragment, SimpleFragment proxyFragment) { 335 id = hierFragment.getId(); 336 parentId = hierFragment.get(Model.HIER_PARENT_KEY); 337 primaryType = hierFragment.getString(Model.MAIN_PRIMARY_TYPE_KEY); 338 isProperty = (Boolean) hierFragment.get(Model.HIER_CHILD_ISPROPERTY_KEY); 339 Serializable ps = proxyFragment == null ? null : proxyFragment.get(Model.PROXY_VERSIONABLE_KEY); 340 if (ps == null) { 341 versionSeriesId = versionFragment == null ? null : versionFragment.get(Model.VERSION_VERSIONABLE_KEY); 342 // may still be null 343 targetId = null; // marks it as a version if versionableId not 344 // null 345 } else { 346 versionSeriesId = ps; 347 targetId = proxyFragment.get(Model.PROXY_TARGET_KEY); 348 } 349 } 350 } 351 352 /** 353 * Deletes a hierarchy and returns information to generate invalidations. 354 * 355 * @param rootInfo info about the root to be deleted with its children (root id, and the rest is for invalidations) 356 * @return info about the descendants removed (including the root) 357 */ 358 List<NodeInfo> remove(NodeInfo rootInfo); 359 360 /** 361 * Processes and returns the invalidations queued for processing by the cache (if any). 362 * <p> 363 * Called pre-transaction by session start or transactionless save; 364 * 365 * @return the invalidations, or {@code null} 366 */ 367 Invalidations receiveInvalidations(); 368 369 /** 370 * Post-transaction invalidations notification. 371 * <p> 372 * Called post-transaction by session commit/rollback or transactionless save. 373 * 374 * @param invalidations the known invalidations to send to others, or {@code null} 375 */ 376 void sendInvalidations(Invalidations invalidations); 377 378 /** 379 * Clears the mapper's cache (if any) 380 * <p> 381 * Called after a rollback, or a manual clear through RepositoryStatus MBean. 382 */ 383 void clearCache(); 384 385 /** 386 * Evaluate the cached elements size 387 * 388 * @since 5.7.2 389 */ 390 long getCacheSize(); 391 392 /** 393 * Rollback the XA Resource. 394 * <p> 395 * This is in the {@link RowMapper} interface because on rollback the cache must be invalidated. 396 */ 397 void rollback(Xid xid) throws XAException; 398 399}