1 16 package org.outerj.daisy.repository.serverimpl; 17 18 import org.outerj.daisy.repository.*; 19 import org.outerj.daisy.repository.user.Role; 20 import org.outerj.daisy.repository.serverimpl.model.LocalSchemaStrategy; 21 import org.outerj.daisy.repository.serverimpl.user.LocalUserManagementStrategy; 22 import org.outerj.daisy.repository.serverimpl.acl.LocalAclStrategy; 23 import org.outerj.daisy.repository.serverimpl.variant.LocalVariantStrategy; 24 import org.outerj.daisy.repository.serverimpl.comment.LocalCommentStrategy; 25 import org.outerj.daisy.repository.commonimpl.*; 26 import org.outerj.daisy.repository.commonimpl.schema.CommonRepositorySchema; 27 import org.outerj.daisy.blobstore.BlobStore; 28 import org.outerj.daisy.cache.DocumentCache; 29 import org.outerj.daisy.query.QueryFactory; 30 import org.outerj.daisy.authentication.UserAuthenticator; 31 import org.outerj.daisy.ftindex.FullTextIndex; 32 import org.outerj.daisy.summary.DocumentSummarizer; 33 import org.outerj.daisy.util.VersionHelper; 34 import org.outerj.daisy.jdbcutil.JdbcHelper; 35 import org.outerj.daisy.jdbcutil.SqlCounter; 36 import org.outerj.daisy.linkextraction.LinkExtractorRegistrar; 37 import org.outerj.daisy.linkextraction.LinkExtractor; 38 import org.apache.avalon.framework.logger.AbstractLogEnabled; 39 import org.apache.avalon.framework.logger.Logger; 40 import org.apache.avalon.framework.activity.Initializable; 41 import org.apache.avalon.framework.activity.Startable; 42 import org.apache.avalon.framework.service.Serviceable; 43 import org.apache.avalon.framework.service.ServiceManager; 44 import org.apache.avalon.framework.service.ServiceException; 45 import org.apache.avalon.framework.configuration.Configurable; 46 import org.apache.avalon.framework.configuration.Configuration; 47 import org.apache.avalon.framework.configuration.ConfigurationException; 48 49 import javax.sql.DataSource ; 50 51 import java.io.IOException ; 52 import java.sql.*; 53 import java.util.*; 54 55 import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList; 56 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap; 57 58 65 public class LocalRepositoryManager extends AbstractLogEnabled implements RepositoryManager, ExtensionRegistrar, 66 LinkExtractorRegistrar, PreSaveHookRegistrar, Serviceable, Initializable, Configurable, Startable { 67 private CommonRepository commonRepository; 68 private DataSource dataSource; 69 private DocumentCache documentCache; 70 private BlobStore blobStore; 71 private UserAuthenticator userAuthenticator; 72 private QueryFactory queryFactory; 73 private Context context; 74 private AuthenticatedUser systemUser; 75 private FullTextIndex fullTextIndex; 76 private DocumentSummarizer documentSummarizer; 77 private JdbcHelper jdbcHelper; 78 private Map registeredExtensions = new ConcurrentReaderHashMap(); 79 private Map registeredLinkExtractors = new ConcurrentReaderHashMap(); 80 private List registeredPreSaveHooks = new CopyOnWriteArrayList(); 81 private long expiredLockJanitorInterval; 82 private Thread expiredLockJanitorThread; 83 89 private final static String DB_SCHEMA_VERSION = "1.5-M1"; 90 91 100 public void service(ServiceManager serviceManager) throws ServiceException { 101 this.dataSource = (DataSource )serviceManager.lookup("datasource"); 102 this.blobStore = (BlobStore)serviceManager.lookup("blobstore"); 103 this.documentCache = (DocumentCache)serviceManager.lookup("documentcache"); 104 this.userAuthenticator = (UserAuthenticator)serviceManager.lookup("userauthenticator"); 105 this.queryFactory = (QueryFactory)serviceManager.lookup("queryfactory"); 106 this.fullTextIndex = (FullTextIndex)serviceManager.lookup("fulltextindex"); 107 this.documentSummarizer = (DocumentSummarizer)serviceManager.lookup("summarizer"); 108 } 109 110 public void configure(Configuration configuration) throws ConfigurationException { 111 this.expiredLockJanitorInterval = configuration.getChild("expiredLockJanitorInterval").getValueAsLong(60000); 112 } 113 114 public void initialize() throws Exception { 115 this.context = new Context(); 116 117 this.systemUser = new AuthenticatedUserImpl(1, null, new long[] { Role.ADMINISTRATOR }, new long[] { Role.ADMINISTRATOR }, "$system"); 119 120 jdbcHelper = JdbcHelper.getInstance(dataSource, getLogger()); 122 123 DocumentStrategy documentStrategy = new LocalDocumentStrategy(context, systemUser, jdbcHelper); 124 LocalSchemaStrategy schemaStrategy = new LocalSchemaStrategy(context, jdbcHelper); 125 LocalAclStrategy aclStrategy = new LocalAclStrategy(context, systemUser, jdbcHelper); 126 LocalUserManagementStrategy userManagementStrategy = new LocalUserManagementStrategy(context, jdbcHelper); 127 LocalVariantStrategy variantStrategy = new LocalVariantStrategy(context, jdbcHelper); 128 LocalCollectionStrategy collectionStrategy = new LocalCollectionStrategy(context, systemUser, jdbcHelper); 129 LocalCommentStrategy commentStrategy = new LocalCommentStrategy(context, systemUser, jdbcHelper); 130 commonRepository = new LocalCommonRepository(documentStrategy, schemaStrategy, aclStrategy, userManagementStrategy, variantStrategy, collectionStrategy, commentStrategy, context, getLogger(), systemUser, documentCache, registeredExtensions, jdbcHelper); 131 commonRepository.addListener(new DocumentCacheInvalidator()); 132 133 userAuthenticator.setUserManager(new RepositoryImpl(commonRepository, systemUser).getUserManager()); 135 136 checkDatabaseSchema(); 137 } 138 139 public void start() throws Exception { 140 expiredLockJanitorThread = new Thread (new ExpiredLockJanitor(), "Daisy Expired Lock Janitor"); 141 expiredLockJanitorThread.start(); 142 } 143 144 public void stop() throws Exception { 145 expiredLockJanitorThread.interrupt(); 146 try { expiredLockJanitorThread.join(); } catch (InterruptedException e) {} 147 } 148 149 public Repository getRepository(final Credentials credentials) throws RepositoryException { 150 AuthenticatedUser user = userAuthenticator.authenticate(credentials); 151 return new RepositoryImpl(commonRepository, user); 152 } 153 154 public synchronized void registerExtension(String name, ExtensionProvider extensionProvider) { 155 if (name == null || name.length() == 0) 156 throw new IllegalArgumentException ("name argument cannot be null or empty string"); 157 158 if (registeredExtensions.containsKey(name)) { 159 throw new RuntimeException ("There is already and extension registered using the name " + name); 160 } 161 162 if (registeredExtensions.containsValue(extensionProvider)) { 163 throw new RuntimeException ("The given extension provider is already registered."); 164 } 165 166 registeredExtensions.put(name, extensionProvider); 167 getLogger().debug("Extension \"" + name + "\" registered with the repository."); 168 } 169 170 public synchronized void unregisterExtension(ExtensionProvider extensionProvider) { 171 String key = null; 172 Iterator entryIt = registeredExtensions.entrySet().iterator(); 173 while (entryIt.hasNext()) { 174 Map.Entry entry = (Map.Entry)entryIt.next(); 175 if (entry.getValue() == extensionProvider) { 176 key = (String )entry.getKey(); 177 break; 178 } 179 } 180 if (key != null) 181 registeredExtensions.remove(key); 182 } 183 184 public synchronized void registerLinkExtractor(String name, String description, LinkExtractor linkExtractor) { 185 if (name == null || name.length() == 0) 186 throw new IllegalArgumentException ("name argument cannot be null or empty string"); 187 if (description == null) 188 throw new IllegalArgumentException ("description argument cannot be null"); 189 if (linkExtractor == null) 190 throw new IllegalArgumentException ("linkExtractor argument cannot be null"); 191 192 RegisteredLinkExtractor extractor = new RegisteredLinkExtractor(); 193 extractor.name = name; 194 extractor.description = description; 195 extractor.linkExtractor = linkExtractor; 196 registeredLinkExtractors.put(name, extractor); 197 } 198 199 static private class RegisteredLinkExtractor { 200 String name; 201 String description; 202 LinkExtractor linkExtractor; 203 } 204 205 public synchronized void registerPreSaveHook(PreSaveHook hook) { 206 if (hook.getName() == null || hook.getName().length() == 0) 207 throw new RepositoryRuntimeException("PreSaveHook.getName() is not allowed to return null or empty string."); 208 209 Iterator it = registeredPreSaveHooks.iterator(); 210 while (it.hasNext()) { 211 if (((PreSaveHook)it.next()).getName().equals(hook.getName())) 212 throw new RepositoryRuntimeException("There is already a PreSaveHook registered with the name " + hook.getName()); 213 } 214 215 registeredPreSaveHooks.add(hook); 216 } 217 218 public synchronized void unregisterPreSaveHook(PreSaveHook hook) { 219 boolean removed = registeredPreSaveHooks.remove(hook); 220 if (!removed) 221 throw new RepositoryRuntimeException("Got a request to remove a non-registered hook: " + hook.getName()); 222 } 223 224 227 public class Context { 228 private SqlCounter docIdCounter = new SqlCounter("document_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 229 private SqlCounter collectionIdCounter = new SqlCounter("collection_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 230 private SqlCounter docTypeCounter = new SqlCounter("documenttype_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 231 private SqlCounter partTypeCounter = new SqlCounter("parttype_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 232 private SqlCounter fieldTypeCounter = new SqlCounter("fieldtype_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 233 private SqlCounter localizedStringCounter = new SqlCounter("localizedstring_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 234 private SqlCounter commentCounter = new SqlCounter("comment_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 235 private SqlCounter eventCounter = new SqlCounter("event_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 236 private SqlCounter userCounter = new SqlCounter("user_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 237 private SqlCounter roleCounter = new SqlCounter("role_sequence", dataSource, LocalRepositoryManager.this.getLogger()); 238 239 private Context() { 240 } 242 243 public DataSource getDataSource() { 244 return dataSource; 245 } 246 247 public QueryFactory getQueryFactory() { 248 return queryFactory; 249 } 250 251 public BlobStore getBlobStore() { 252 return blobStore; 253 } 254 255 public FullTextIndex getFullTextIndex() { 256 return fullTextIndex; 257 } 258 259 public UserAuthenticator getUserAuthenticator() { 260 return userAuthenticator; 261 } 262 263 public DocumentSummarizer getDocumentSummarizer() { 264 return documentSummarizer; 265 } 266 267 public CommonRepositorySchema getRepositorySchema() { 268 return commonRepository.getRepositorySchema(); 269 } 270 271 public CommonRepository getCommonRepository() { 272 return commonRepository; 273 } 274 275 public Logger getLogger() { 276 return LocalRepositoryManager.this.getLogger(); 277 } 278 279 public long getNextDocumentId() throws SQLException { 280 return docIdCounter.getNextId(); 281 } 282 283 public long getNextCollectionId() throws SQLException { 284 return collectionIdCounter.getNextId(); 285 } 286 287 public long getNextDocumentTypeId() throws SQLException { 288 return docTypeCounter.getNextId(); 289 } 290 291 public long getNextPartTypeId() throws SQLException { 292 return partTypeCounter.getNextId(); 293 } 294 295 public long getNextFieldTypeId() throws SQLException { 296 return fieldTypeCounter.getNextId(); 297 } 298 299 public long getNextLocalizedStringId() throws SQLException { 300 return localizedStringCounter.getNextId(); 301 } 302 303 public long getNextCommentId() throws SQLException { 304 return commentCounter.getNextId(); 305 } 306 307 public long getNextEventId() throws SQLException { 308 return eventCounter.getNextId(); 309 } 310 311 public long getNextUserId() throws SQLException { 312 return userCounter.getNextId(); 313 } 314 315 public long getNextRoleId() throws SQLException { 316 return roleCounter.getNextId(); 317 } 318 319 public LinkExtractor getLinkExtractor(String name) { 320 RegisteredLinkExtractor extractor = (RegisteredLinkExtractor)registeredLinkExtractors.get(name); 321 return extractor != null ? extractor.linkExtractor : null; 322 } 323 324 public LinkExtractorInfo[] getLinkExtractors() { 325 RegisteredLinkExtractor[] extractors = (RegisteredLinkExtractor[])registeredLinkExtractors.values().toArray(new RegisteredLinkExtractor[0]); 326 LinkExtractorInfo[] extractorInfos = new LinkExtractorInfo[extractors.length]; 327 for (int i = 0; i < extractorInfos.length; i++) { 328 extractorInfos[i] = new LinkExtractorInfoImpl(extractors[i].name, extractors[i].description); 329 } 330 return extractorInfos; 331 } 332 333 public List getPreSaveHooks() { 334 return registeredPreSaveHooks; 335 } 336 } 337 338 341 private class DocumentCacheInvalidator implements RepositoryListener { 342 public void repositoryEvent(RepositoryEventType eventType, long id, long updateCount) { 343 if (eventType == RepositoryEventType.DOCUMENT_UPDATED 344 || eventType == RepositoryEventType.DOCUMENT_DELETED) { 345 documentCache.remove(id); 346 documentCache.removeAvailableVariants(id); 347 } else if (eventType == RepositoryEventType.COLLECTION_DELETED 348 || eventType == RepositoryEventType.COLLECTION_UPDATED) { 349 documentCache.clear(); 354 } 355 } 356 357 public void variantEvent(DocumentVariantEventType eventType, long documentId, long branchId, long languageId, long updateCount) { 358 if (eventType == DocumentVariantEventType.VERSION_STATE_CHANGED 359 || eventType == DocumentVariantEventType.DOCUMENT_VARIANT_CREATED 360 || eventType == DocumentVariantEventType.DOCUMENT_VARIANT_DELETED 361 || eventType == DocumentVariantEventType.DOCUMENT_VARIANT_UPDATED) { 362 documentCache.remove(documentId, branchId, languageId); 363 documentCache.removeAvailableVariants(documentId); 364 } else if (eventType == DocumentVariantEventType.LOCK_CHANGE) { 365 DocumentImpl document = documentCache.get(documentId, branchId, languageId); 366 if (document != null) { 367 document.clearLockInfo(); 368 } 369 } 370 } 371 } 372 373 private class ExpiredLockJanitor implements Runnable { 374 private final String SHUTDOWN_MESSAGE = "ExpiredLockJanitor shutting down."; 375 public void run() { 376 try { 377 while (true) { 378 Thread.sleep(expiredLockJanitorInterval); 379 Connection conn = null; 380 PreparedStatement stmt = null; 381 try { 382 conn = dataSource.getConnection(); 383 stmt = conn.prepareStatement("select doc_id, branch_id, lang_id from locks where time_expires is not null and time_expires < ?"); 384 stmt.setTimestamp(1, new Timestamp(System.currentTimeMillis())); 385 ResultSet rs = stmt.executeQuery(); 386 while (rs.next()) { 387 if (Thread.interrupted()) { 388 getLogger().info(SHUTDOWN_MESSAGE); 389 return; 390 } 391 long docId = rs.getLong("doc_id"); 392 long branchId = rs.getLong("branch_id"); 393 long languageId = rs.getLong("lang_id"); 394 try { 395 commonRepository.getDocument(docId, branchId, languageId, false, systemUser).getLockInfo(true); 396 } catch (DocumentVariantNotFoundException e) { 397 } catch (DocumentNotFoundException e) { 399 } catch (RepositoryException e) { 401 getLogger().error("Error trying to update expired lock info for document " + docId + ", branch " + branchId + ", language " + languageId); 402 } 403 } 404 } catch (Throwable e) { 405 getLogger().error("Exception occured in ExpiredLockJanitor.", e); 406 } finally { 407 jdbcHelper.closeStatement(stmt); 408 jdbcHelper.closeConnection(conn); 409 } 410 } 411 } catch (InterruptedException e) { 412 getLogger().info(SHUTDOWN_MESSAGE); 413 } 414 } 415 } 416 417 private void checkDatabaseSchema() throws RepositoryException { 418 Connection conn = null; 419 PreparedStatement stmt = null; 420 try { 421 conn = dataSource.getConnection(); 422 stmt = conn.prepareStatement("select propvalue from daisy_system where propname = 'schema_version'"); 423 ResultSet rs = stmt.executeQuery(); 424 if (!rs.next()) 425 throw new RepositoryException("No schema_version found in daisy_system table."); 426 String version = rs.getString(1); 427 if (!DB_SCHEMA_VERSION.equals(version)) 428 throw new RepositoryException("The repository database schema is not at the correct version: found :\"" + version + "\", expected: \"" + DB_SCHEMA_VERSION + "\"."); 429 } catch (SQLException e) { 430 throw new RepositoryException("Error getting schema version information.", e); 431 } catch (RepositoryException e) { 432 throw e; 433 } finally { 434 jdbcHelper.closeStatement(stmt); 435 jdbcHelper.closeConnection(conn); 436 } 437 } 438 439 public String getRepositoryServerVersion() { 440 return commonRepository.getServerVersion(systemUser); 441 } 442 } 443 | Popular Tags |