KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > repository > serverimpl > LocalRepositoryManager


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

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 JavaDoc;
50
51 import java.io.IOException JavaDoc;
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 /**
59  * @avalon.component version="1.0" name="repository-manager" lifestyle="singleton"
60  * @avalon.service type="org.outerj.daisy.repository.RepositoryManager"
61  * @avalon.service type="org.outerj.daisy.repository.ExtensionRegistrar"
62  * @avalon.service type="org.outerj.daisy.linkextraction.LinkExtractorRegistrar"
63  * @avalon.service type="org.outerj.daisy.repository.PreSaveHookRegistrar"
64  */

65 public class LocalRepositoryManager extends AbstractLogEnabled implements RepositoryManager, ExtensionRegistrar,
66         LinkExtractorRegistrar, PreSaveHookRegistrar, Serviceable, Initializable, Configurable, Startable {
67     private CommonRepository commonRepository;
68     private DataSource JavaDoc 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 JavaDoc expiredLockJanitorThread;
83     /**
84      * The database schema version number. Not necessarily the same as the Daisy version number.
85      * This number only gets augmented on releases where the schema actually changes.
86      * If changed here, also needs to be changed in daisy-data.xml and the database
87      * upgrade script.
88      */

89     private final static String JavaDoc DB_SCHEMA_VERSION = "1.5-M1";
90
91     /**
92      * @avalon.dependency key="datasource" type="javax.sql.DataSource"
93      * @avalon.dependency key="blobstore" type="org.outerj.daisy.blobstore.BlobStore"
94      * @avalon.dependency key="documentcache" type="org.outerj.daisy.cache.DocumentCache"
95      * @avalon.dependency key="userauthenticator" type="org.outerj.daisy.authentication.UserAuthenticator"
96      * @avalon.dependency key="queryfactory" type="org.outerj.daisy.query.QueryFactory"
97      * @avalon.dependency key="fulltextindex" type="org.outerj.daisy.ftindex.FullTextIndex"
98      * @avalon.dependency key="summarizer" type="org.outerj.daisy.summary.DocumentSummarizer"
99      */

100     public void service(ServiceManager serviceManager) throws ServiceException {
101         this.dataSource = (DataSource JavaDoc)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 JavaDoc {
115         this.context = new Context();
116
117         // The sytem user is a built-in user which has ID 1
118
this.systemUser = new AuthenticatedUserImpl(1, null, new long[] { Role.ADMINISTRATOR }, new long[] { Role.ADMINISTRATOR }, "$system");
119
120         // Get the database-specific JdbcHelper
121
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         // supply the user
134
userAuthenticator.setUserManager(new RepositoryImpl(commonRepository, systemUser).getUserManager());
135
136         checkDatabaseSchema();
137     }
138
139     public void start() throws Exception JavaDoc {
140         expiredLockJanitorThread = new Thread JavaDoc(new ExpiredLockJanitor(), "Daisy Expired Lock Janitor");
141         expiredLockJanitorThread.start();
142     }
143
144     public void stop() throws Exception JavaDoc {
145         expiredLockJanitorThread.interrupt();
146         try { expiredLockJanitorThread.join(); } catch (InterruptedException JavaDoc 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 JavaDoc name, ExtensionProvider extensionProvider) {
155         if (name == null || name.length() == 0)
156             throw new IllegalArgumentException JavaDoc("name argument cannot be null or empty string");
157
158         if (registeredExtensions.containsKey(name)) {
159             throw new RuntimeException JavaDoc("There is already and extension registered using the name " + name);
160         }
161
162         if (registeredExtensions.containsValue(extensionProvider)) {
163             throw new RuntimeException JavaDoc("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 JavaDoc 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 JavaDoc)entry.getKey();
177                 break;
178             }
179         }
180         if (key != null)
181             registeredExtensions.remove(key);
182     }
183
184     public synchronized void registerLinkExtractor(String JavaDoc name, String JavaDoc description, LinkExtractor linkExtractor) {
185         if (name == null || name.length() == 0)
186             throw new IllegalArgumentException JavaDoc("name argument cannot be null or empty string");
187         if (description == null)
188             throw new IllegalArgumentException JavaDoc("description argument cannot be null");
189         if (linkExtractor == null)
190             throw new IllegalArgumentException JavaDoc("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 JavaDoc name;
201         String JavaDoc 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     /**
225      * Context information for the document implementation
226      */

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             // private constructor to make sure no-one else can create this
241
}
242
243         public DataSource JavaDoc 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 JavaDoc 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     /**
339      * Event listener that removes a document from the cache when it has been updated.
340      */

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                 // If a collection is deleted (or its name is changed), we would
350
// need to remove/update all cached document objects referencing
351
// that collection.
352
// To avoid this complexity, we simply clear the entire cache.
353
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 JavaDoc {
374         private final String JavaDoc 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                                 // ignore
398
} catch (DocumentNotFoundException e) {
399                                 // ignore
400
} 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 JavaDoc e) {
405                         getLogger().error("Exception occured in ExpiredLockJanitor.", e);
406                     } finally {
407                         jdbcHelper.closeStatement(stmt);
408                         jdbcHelper.closeConnection(conn);
409                     }
410                 }
411             } catch (InterruptedException JavaDoc 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 JavaDoc 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 JavaDoc getRepositoryServerVersion() {
440         return commonRepository.getServerVersion(systemUser);
441     }
442 }
443
Popular Tags