001/* 002 * (C) Copyright 2014-2018 Nuxeo (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 */ 019package org.nuxeo.ecm.core.storage.dbs; 020 021import java.util.ArrayDeque; 022import java.util.Collection; 023import java.util.Deque; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Set; 027 028import org.apache.commons.lang3.StringUtils; 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.nuxeo.ecm.core.api.Lock; 032import org.nuxeo.ecm.core.api.NuxeoException; 033import org.nuxeo.ecm.core.api.lock.LockManager; 034import org.nuxeo.ecm.core.api.repository.FulltextConfiguration; 035import org.nuxeo.ecm.core.blob.BlobManager; 036import org.nuxeo.ecm.core.model.Session; 037import org.nuxeo.ecm.core.schema.DocumentType; 038import org.nuxeo.ecm.core.schema.SchemaManager; 039import org.nuxeo.ecm.core.schema.TypeConstants; 040import org.nuxeo.ecm.core.schema.types.ComplexType; 041import org.nuxeo.ecm.core.schema.types.CompositeType; 042import org.nuxeo.ecm.core.schema.types.Field; 043import org.nuxeo.ecm.core.schema.types.ListType; 044import org.nuxeo.ecm.core.schema.types.Schema; 045import org.nuxeo.ecm.core.schema.types.Type; 046import org.nuxeo.ecm.core.storage.FulltextConfigurationFactory; 047import org.nuxeo.ecm.core.storage.FulltextDescriptor; 048import org.nuxeo.ecm.core.storage.lock.LockManagerService; 049import org.nuxeo.runtime.api.Framework; 050 051/** 052 * Provides sharing behavior for repository sessions and other basic functions. 053 * 054 * @since 5.9.4 055 */ 056public abstract class DBSRepositoryBase implements DBSRepository { 057 058 private static final Log log = LogFactory.getLog(DBSRepositoryBase.class); 059 060 public static final String TYPE_ROOT = "Root"; 061 062 // change to have deterministic pseudo-UUID generation for debugging 063 public static final boolean DEBUG_UUIDS = false; 064 065 public static final String UUID_ZERO = "00000000-0000-0000-0000-000000000000"; 066 067 public static final String UUID_ZERO_DEBUG = "UUID_0"; 068 069 /** 070 * Type of id to used for documents. 071 * 072 * @since 8.3 073 */ 074 public enum IdType { 075 /** Random UUID stored in a string. */ 076 varchar, 077 /** Random UUID stored as a native UUID type. */ 078 uuid, 079 /** Integer sequence maintained by the database. */ 080 sequence, 081 /** 082 * Integer sequence maintained by the database, in a pseudo-random order, as hex. 083 * 084 * @since 11.1 085 */ 086 sequenceHexRandomized, 087 } 088 089 /** @since 8.3 */ 090 protected final IdType idType; 091 092 protected final String repositoryName; 093 094 protected final FulltextConfiguration fulltextConfiguration; 095 096 protected final BlobManager blobManager; 097 098 protected LockManager lockManager; 099 100 protected final boolean changeTokenEnabled; 101 102 /** 103 * @since 7.4 : used to know if the LockManager was provided by this repository or externally 104 */ 105 protected boolean selfRegisteredLockManager = false; 106 107 public DBSRepositoryBase(String repositoryName, DBSRepositoryDescriptor descriptor) { 108 this.repositoryName = repositoryName; 109 String idt = descriptor.idType; 110 List<IdType> allowed = getAllowedIdTypes(); 111 if (StringUtils.isBlank(idt)) { 112 idt = allowed.get(0).name(); 113 } 114 try { 115 idType = IdType.valueOf(idt); 116 if (!allowed.contains(idType)) { 117 throw new IllegalArgumentException("Invalid id type: " + idt); 118 } 119 } catch (IllegalArgumentException e) { 120 throw new NuxeoException("Unknown id type: " + idt + ", allowed: " + allowed); 121 } 122 FulltextDescriptor fulltextDescriptor = descriptor.getFulltextDescriptor(); 123 if (fulltextDescriptor.getFulltextDisabled()) { 124 fulltextConfiguration = null; 125 } else { 126 fulltextConfiguration = FulltextConfigurationFactory.make(fulltextDescriptor); 127 } 128 changeTokenEnabled = descriptor.isChangeTokenEnabled(); 129 blobManager = Framework.getService(BlobManager.class); 130 initBlobsPaths(); 131 initLockManager(); 132 } 133 134 /** Gets the allowed id types for this DBS repository. The first one is the default. */ 135 public abstract List<IdType> getAllowedIdTypes(); 136 137 /** @since 11.1 */ 138 public IdType getIdType() { 139 return idType; 140 } 141 142 @Override 143 public void shutdown() { 144 if (selfRegisteredLockManager) { 145 LockManagerService lms = Framework.getService(LockManagerService.class); 146 if (lms != null) { 147 lms.unregisterLockManager(getLockManagerName()); 148 } 149 } 150 } 151 152 @Override 153 public String getName() { 154 return repositoryName; 155 } 156 157 @Override 158 public FulltextConfiguration getFulltextConfiguration() { 159 return fulltextConfiguration; 160 } 161 162 protected String getLockManagerName() { 163 // TODO configure in repo descriptor 164 return getName(); 165 } 166 167 protected void initLockManager() { 168 String lockManagerName = getLockManagerName(); 169 LockManagerService lockManagerService = Framework.getService(LockManagerService.class); 170 lockManager = lockManagerService.getLockManager(lockManagerName); 171 if (lockManager == null) { 172 // no descriptor, use DBS repository intrinsic lock manager 173 lockManager = this; 174 log.info("Repository " + repositoryName + " using own lock manager"); 175 lockManagerService.registerLockManager(lockManagerName, lockManager); 176 selfRegisteredLockManager = true; 177 } else { 178 selfRegisteredLockManager = false; 179 log.info("Repository " + repositoryName + " using lock manager " + lockManager); 180 } 181 } 182 183 @Override 184 public LockManager getLockManager() { 185 return lockManager; 186 } 187 188 @Override 189 public Lock getLock(String id) { 190 try (DBSConnection connection = getConnection()) { 191 return connection.getLock(id); 192 } 193 } 194 195 @Override 196 public Lock setLock(String id, Lock lock) { 197 try (DBSConnection connection = getConnection()) { 198 return connection.setLock(id, lock); 199 } 200 } 201 202 @Override 203 public Lock removeLock(String id, String owner) { 204 try (DBSConnection connection = getConnection()) { 205 return connection.removeLock(id, owner); 206 } 207 } 208 209 protected abstract void initBlobsPaths(); 210 211 /** Finds the paths for all blobs in all document types. */ 212 protected static abstract class BlobFinder { 213 214 protected final Set<String> schemaDone = new HashSet<>(); 215 216 protected final Deque<String> path = new ArrayDeque<>(); 217 218 public void visit() { 219 SchemaManager schemaManager = Framework.getService(SchemaManager.class); 220 // document types 221 for (DocumentType docType : schemaManager.getDocumentTypes()) { 222 visitSchemas(docType.getSchemas()); 223 } 224 // mixins 225 for (CompositeType type : schemaManager.getFacets()) { 226 visitSchemas(type.getSchemas()); 227 } 228 } 229 230 protected void visitSchemas(Collection<Schema> schemas) { 231 for (Schema schema : schemas) { 232 if (schemaDone.add(schema.getName())) { 233 visitComplexType(schema); 234 } 235 } 236 } 237 238 protected void visitComplexType(ComplexType complexType) { 239 if (TypeConstants.isContentType(complexType)) { 240 recordBlobPath(); 241 return; 242 } 243 for (Field field : complexType.getFields()) { 244 visitField(field); 245 } 246 } 247 248 /** Records a blob path, stored in the {@link #path} field. */ 249 protected abstract void recordBlobPath(); 250 251 protected void visitField(Field field) { 252 Type type = field.getType(); 253 if (type.isSimpleType()) { 254 // scalar 255 // assume no bare binary exists 256 } else if (type.isComplexType()) { 257 // complex property 258 String name = field.getName().getPrefixedName(); 259 path.addLast(name); 260 visitComplexType((ComplexType) type); 261 path.removeLast(); 262 } else { 263 // array or list 264 Type fieldType = ((ListType) type).getFieldType(); 265 if (fieldType.isSimpleType()) { 266 // array 267 // assume no array of bare binaries exist 268 } else { 269 // complex list 270 String name = field.getName().getPrefixedName(); 271 path.addLast(name); 272 visitComplexType((ComplexType) fieldType); 273 path.removeLast(); 274 } 275 } 276 } 277 } 278 279 @Override 280 public BlobManager getBlobManager() { 281 return blobManager; 282 } 283 284 @Override 285 public boolean isFulltextDisabled() { 286 return fulltextConfiguration == null; 287 } 288 289 @Override 290 public boolean isFulltextStoredInBlob() { 291 return fulltextConfiguration != null && fulltextConfiguration.fulltextStoredInBlob; 292 } 293 294 @Override 295 public boolean isFulltextSearchDisabled() { 296 return isFulltextDisabled() || isFulltextStoredInBlob() || fulltextConfiguration.fulltextSearchDisabled; 297 } 298 299 @Override 300 public boolean isChangeTokenEnabled() { 301 return changeTokenEnabled; 302 } 303 304 @Override 305 public Session getSession() { 306 return new DBSSession(this); 307 } 308 309}