diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index 58739f596a..4c104d7706 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -64,6 +64,12 @@ --> + + + + - + + + - - - @@ -131,7 +130,8 @@ - + + @@ -197,6 +197,9 @@ classpath*:alfresco/module/*/log4j.properties + + classpath*:alfresco/enterprise/*-log4j.properties + classpath*:alfresco/extension/*-log4j.properties diff --git a/config/alfresco/scheduled-jobs-context.xml b/config/alfresco/scheduled-jobs-context.xml index 44b5eb0ec2..1db34c306a 100644 --- a/config/alfresco/scheduled-jobs-context.xml +++ b/config/alfresco/scheduled-jobs-context.xml @@ -3,6 +3,24 @@ + + + + + + classpath:alfresco/domain/quartz.properties + classpath*:alfresco/enterprise/*-quartz.properties + classpath*:alfresco/extension/*-quartz.properties + + + + + + + + + + @@ -10,8 +28,8 @@ true - - classpath:alfresco/domain/quartz.properties + + DefaultScheduler diff --git a/source/java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java b/source/java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java index 43a01efc1d..9a02579eb6 100644 --- a/source/java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java @@ -40,7 +40,7 @@ import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; /** * Abstract base class that provides a set of tests for implementations @@ -54,7 +54,7 @@ import org.springframework.context.ApplicationContext; */ public abstract class AbstractReadOnlyContentStoreTest extends TestCase { - private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + protected static final ConfigurableApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private static Log logger = LogFactory.getLog(AbstractReadOnlyContentStoreTest.class); diff --git a/source/java/org/alfresco/repo/content/RoutingContentService.java b/source/java/org/alfresco/repo/content/RoutingContentService.java index fdb13d6f85..fe4a9b884b 100644 --- a/source/java/org/alfresco/repo/content/RoutingContentService.java +++ b/source/java/org/alfresco/repo/content/RoutingContentService.java @@ -69,6 +69,10 @@ import org.alfresco.util.Pair; import org.alfresco.util.TempFileProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; /** @@ -77,7 +81,7 @@ import org.apache.commons.logging.LogFactory; * * @author Derek Hulley */ -public class RoutingContentService implements ContentService +public class RoutingContentService implements ContentService, ApplicationContextAware { private static Log logger = LogFactory.getLog(RoutingContentService.class); @@ -85,6 +89,7 @@ public class RoutingContentService implements ContentService private NodeService nodeService; private AVMService avmService; private RetryingTransactionHelper transactionHelper; + private ApplicationContext applicationContext; /** a registry of all available content transformers */ private ContentTransformerRegistry transformerRegistry; @@ -105,14 +110,6 @@ public class RoutingContentService implements ContentService ClassPolicyDelegate onContentUpdateDelegate; ClassPolicyDelegate onContentReadDelegate; - /** - * Default constructor sets up a temporary store - */ - public RoutingContentService() - { - this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath()); - } - /** * @deprecated Replaced by {@link #setRetryingTransactionHelper(RetryingTransactionHelper)} */ @@ -161,11 +158,24 @@ public class RoutingContentService implements ContentService this.imageMagickContentTransformer = imageMagickContentTransformer; } + + /* (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) + */ + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + /** * Service initialise */ public void init() { + // Set up a temporary store + this.tempStore = new FileContentStore((ConfigurableApplicationContext) this.applicationContext, + TempFileProvider.getTempDir().getAbsolutePath()); + // Bind on update properties behaviour this.policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"), diff --git a/source/java/org/alfresco/repo/content/RoutingContentStoreTest.java b/source/java/org/alfresco/repo/content/RoutingContentStoreTest.java index 81cfb2e443..3fb6a2c081 100644 --- a/source/java/org/alfresco/repo/content/RoutingContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/RoutingContentStoreTest.java @@ -65,13 +65,13 @@ public class RoutingContentStoreTest extends AbstractWritableContentStoreTest File tempDir = TempFileProvider.getTempDir(); // Create a subdirectory for A File storeADir = new File(tempDir, "A"); - storeA = new FileContentStore(storeADir); + storeA = new FileContentStore(ctx, storeADir); // Create a subdirectory for B File storeBDir = new File(tempDir, "B"); - storeB = new FileContentStore(storeBDir); + storeB = new FileContentStore(ctx, storeBDir); // Create a subdirectory for C File storeCDir = new File(tempDir, "C"); - storeC = new DumbReadOnlyFileStore(new FileContentStore(storeCDir)); + storeC = new DumbReadOnlyFileStore(new FileContentStore(ctx, storeCDir)); // No subdirectory for D storeD = new SupportsNoUrlFormatStore(); // Create the routing store diff --git a/source/java/org/alfresco/repo/content/TenantRoutingFileContentStore.java b/source/java/org/alfresco/repo/content/TenantRoutingFileContentStore.java index 40f504caa7..d202287a3f 100755 --- a/source/java/org/alfresco/repo/content/TenantRoutingFileContentStore.java +++ b/source/java/org/alfresco/repo/content/TenantRoutingFileContentStore.java @@ -36,18 +36,23 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.Tenant; import org.alfresco.repo.tenant.TenantDeployer; import org.alfresco.repo.tenant.TenantService; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; /** * Content Store that supports tenant routing, if multi-tenancy is enabled. * * Note: Need to initialise before the dictionary service, in the case that models are dynamically loaded for the tenant. */ -public class TenantRoutingFileContentStore extends AbstractRoutingContentStore implements TenantDeployer +public class TenantRoutingFileContentStore extends AbstractRoutingContentStore implements TenantDeployer, ApplicationContextAware { Map tenantFileStores = new HashMap(); private String defaultRootDirectory; private TenantService tenantService; + private ApplicationContext applicationContext; public void setDefaultRootDir(String defaultRootDirectory) @@ -59,6 +64,16 @@ public class TenantRoutingFileContentStore extends AbstractRoutingContentStore i { this.tenantService = tenantService; } + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context. + * ApplicationContext) + */ + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } protected ContentStore selectWriteStore(ContentContext ctx) { @@ -114,7 +129,8 @@ public class TenantRoutingFileContentStore extends AbstractRoutingContentStore i tenantDomain = tenant.getTenantDomain(); } - putTenantFileStore(tenantDomain, new FileContentStore(new File(rootDir))); + putTenantFileStore(tenantDomain, new FileContentStore((ConfigurableApplicationContext) this.applicationContext, + new File(rootDir))); } public void destroy() diff --git a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java index 9662609c62..bee446676a 100644 --- a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java +++ b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java @@ -29,6 +29,8 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; +import junit.framework.TestCase; + import org.alfresco.repo.avm.AVMNodeDAO; import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.filestore.FileContentStore; @@ -42,9 +44,7 @@ import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.TempFileProvider; -import org.springframework.context.ApplicationContext; - -import junit.framework.TestCase; +import org.springframework.context.ConfigurableApplicationContext; /** * @see org.alfresco.repo.content.cleanup.ContentStoreCleaner @@ -53,7 +53,7 @@ import junit.framework.TestCase; */ public class ContentStoreCleanerTest extends TestCase { - private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + private static ConfigurableApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private ContentStoreCleaner cleaner; private ContentStore store; @@ -71,7 +71,7 @@ public class ContentStoreCleanerTest extends TestCase ContentUrlDAO contentUrlDAO = (ContentUrlDAO) ctx.getBean("contentUrlDAO"); // we need a store - store = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath()); + store = new FileContentStore(ctx, TempFileProvider.getTempDir().getAbsolutePath()); // and a listener listener = new DummyCleanerListener(); // initialise record of deleted URLs diff --git a/source/java/org/alfresco/repo/content/filestore/FileContentStore.java b/source/java/org/alfresco/repo/content/filestore/FileContentStore.java index b7629e719c..b221577067 100644 --- a/source/java/org/alfresco/repo/content/filestore/FileContentStore.java +++ b/source/java/org/alfresco/repo/content/filestore/FileContentStore.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2008 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,7 @@ * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing + * FLOSS exception. You should have received a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ @@ -43,6 +43,10 @@ import org.alfresco.util.GUID; import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; /** * Provides a store of node content directly to the file system. The writers @@ -53,7 +57,7 @@ import org.apache.commons.logging.LogFactory; * * @author Derek Hulley */ -public class FileContentStore extends AbstractContentStore +public class FileContentStore extends AbstractContentStore implements ApplicationListener { /** * store is the new prefix for file content URLs @@ -69,21 +73,24 @@ public class FileContentStore extends AbstractContentStore private boolean readOnly; /** - * @param rootDirectoryStr the root under which files will be stored. - * The directory will be created if it does not exist. - * + * Private: for Spring-constructed instances only. + * + * @param rootDirectoryStr + * the root under which files will be stored. The directory will be created if it does not exist. * @see FileContentStore#FileContentStore(File) */ - public FileContentStore(String rootDirectoryStr) + private FileContentStore(String rootDirectoryStr) { this(new File(rootDirectoryStr)); } /** - * @param rootDirectory the root under which files will be stored. - * The directory will be created if it does not exist. + * Private: for Spring-constructed instances only. + * + * @param rootDirectory + * the root under which files will be stored. The directory will be created if it does not exist. */ - public FileContentStore(File rootDirectory) + private FileContentStore(File rootDirectory) { if (!rootDirectory.exists()) { @@ -98,6 +105,36 @@ public class FileContentStore extends AbstractContentStore readOnly = false; } + /** + * Public constructor for programmatic use. + * + * @param context + * application context through which events can be published + * @param rootDirectoryStr + * the root under which files will be stored. The directory will be created if it does not exist. + * @see FileContentStore#FileContentStore(File) + */ + public FileContentStore(ConfigurableApplicationContext context, String rootDirectoryStr) + { + this(rootDirectoryStr); + publishEvent(context); + } + + /** + * Public constructor for programmatic use. + * + * @param context + * application context through which events can be published + * @param rootDirectory + * the root under which files will be stored. The directory will be created if it does not exist. + */ + public FileContentStore(ConfigurableApplicationContext context, File rootDirectory) + { + this(rootDirectory); + publishEvent(context); + } + + public String toString() { StringBuilder sb = new StringBuilder(36); @@ -159,9 +196,9 @@ public class FileContentStore extends AbstractContentStore * @param newContentUrl the specific URL to use, which may not be in use * @return Returns a new and unique file * @throws IOException - * if the file or parent directories couldn't be created or if the URL is already in use. + * if the file or parent directories couldn't be created or if the URL is already in use. * @throws UnsupportedOperationException - * if the store is read-only + * if the store is read-only * * @see #setReadOnly(boolean) */ @@ -298,7 +335,7 @@ public class FileContentStore extends AbstractContentStore } /** - * @return Returns true always + * @return Returns true always */ public boolean isWriteSupported() { @@ -358,6 +395,12 @@ public class FileContentStore extends AbstractContentStore } /** + * Returns a writer onto a location based on the date. + * + * @param existingContentReader + * the existing content reader + * @param newContentUrl + * the new content url * @return Returns a writer onto a location based on the date */ public ContentWriter getWriterInternal(ContentReader existingContentReader, String newContentUrl) @@ -396,6 +439,17 @@ public class FileContentStore extends AbstractContentStore } } + /** + * Gets the urls. + * + * @param createdAfter + * the created after date + * @param createdBefore + * the created before dat6e + * @param handler + * the handler + * @return the urls + */ public void getUrls(Date createdAfter, Date createdBefore, ContentUrlHandler handler) { // recursively get all files within the root @@ -409,11 +463,12 @@ public class FileContentStore extends AbstractContentStore } /** + * Returns a list of all files within the given directory and all subdirectories. * @param directory the current directory to get the files from * @param handler the callback to use for each URL * @param createdAfter only get URLs for content create after this date * @param createdBefore only get URLs for content created before this date - * @return Returns a list of all files within the given directory and all subdirectories + * @return a list of all files within the given directory and all subdirectories */ private void getUrls(File directory, ContentUrlHandler handler, Date createdAfter, Date createdBefore) { @@ -516,4 +571,41 @@ public class FileContentStore extends AbstractContentStore // done return newContentUrl; } + + /** + * Publishes an event to the application context that will notify any interested parties of the existence of this + * content store. + * + * @param context + * the application context + */ + private void publishEvent(ConfigurableApplicationContext context) + { + // If the context isn't running yet, we have to wait until the refresh event + try + { + // Neither context.isActive() or context.isRunning() seem to detect whether the refresh() has completed + context.publishEvent(new FileContentStoreCreatedEvent(this, rootDirectory)); + } + catch (IllegalStateException e) + { + context.addApplicationListener(this); + } + } + + /* + * (non-Javadoc) + * @see + * org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + public void onApplicationEvent(ApplicationEvent event) + { + // Once the context has been refreshed, we tell other interested beans about the existence of this content store + // (e.g. for monitoring purposes) + if (event instanceof ContextRefreshedEvent) + { + ((ContextRefreshedEvent) event).getApplicationContext().publishEvent( + new FileContentStoreCreatedEvent(this, rootDirectory)); + } + } } diff --git a/source/java/org/alfresco/repo/content/filestore/FileContentStoreCreatedEvent.java b/source/java/org/alfresco/repo/content/filestore/FileContentStoreCreatedEvent.java new file mode 100644 index 0000000000..3bb3f0fa11 --- /dev/null +++ b/source/java/org/alfresco/repo/content/filestore/FileContentStoreCreatedEvent.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.content.filestore; + +import java.io.File; + +import org.springframework.context.ApplicationEvent; + +/** + * A class of event that notifies the listener of the existence of a {@link FileContentStore}. Useful for Monitoring + * purposes. + * + * @author dward + */ +public class FileContentStoreCreatedEvent extends ApplicationEvent +{ + private static final long serialVersionUID = 7090069096441126707L; + + /** The root directory of the store. */ + private final File rootDirectory; + + /** + * The Constructor. + * + * @param source + * the source content store + * @param rootDirectory + * the root directory + */ + public FileContentStoreCreatedEvent(FileContentStore source, File rootDirectory) + { + super(source); + this.rootDirectory = rootDirectory; + } + + /** + * Gets the root directory. + * + * @return the root directory + */ + public File getRootDirectory() + { + return this.rootDirectory; + } +} diff --git a/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java b/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java index 8a9cbdfc64..25c6c92d67 100644 --- a/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java @@ -52,7 +52,7 @@ public class FileContentStoreTest extends AbstractWritableContentStoreTest // create a store that uses a subdirectory of the temp directory File tempDir = TempFileProvider.getTempDir(); - store = new FileContentStore( + store = new FileContentStore(ctx, tempDir.getAbsolutePath() + File.separatorChar + getName()); diff --git a/source/java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java b/source/java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java index 4a6a5a3c3a..f376b2dd24 100644 --- a/source/java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java @@ -49,7 +49,7 @@ public class NoRandomAccessFileContentStoreTest extends AbstractWritableContentS // create a store that uses a subdirectory of the temp directory File tempDir = TempFileProvider.getTempDir(); - store = new FileContentStore( + store = new FileContentStore(ctx, tempDir.getAbsolutePath() + File.separatorChar + getName()); diff --git a/source/java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java b/source/java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java index af0bda971e..89a60ebe94 100644 --- a/source/java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java @@ -50,7 +50,7 @@ public class ReadOnlyFileContentStoreTest extends AbstractReadOnlyContentStoreTe // create a store that uses a subdirectory of the temp directory File tempDir = TempFileProvider.getTempDir(); - store = new FileContentStore( + store = new FileContentStore(ctx, tempDir.getAbsolutePath() + File.separatorChar + getName()); diff --git a/source/java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java b/source/java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java index 0f9f710838..9e92944486 100644 --- a/source/java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java +++ b/source/java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java @@ -30,7 +30,6 @@ import java.util.Set; import junit.framework.TestCase; -import org.alfresco.repo.content.AbstractContentStore; import org.alfresco.repo.content.ContentContext; import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.ContentStore.ContentUrlHandler; @@ -38,6 +37,8 @@ import org.alfresco.repo.content.filestore.FileContentStore; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.util.GUID; import org.alfresco.util.TempFileProvider; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.StaticApplicationContext; /** * Tests the content store replicator. @@ -60,13 +61,17 @@ public class ContentStoreReplicatorTest extends TestCase { super.setUp(); + // Create a dummy context for message broadcasting + StaticApplicationContext ctx = new StaticApplicationContext(); + ctx.refresh(); + File tempDir = TempFileProvider.getTempDir(); // create the source file store String storeDir = tempDir.getAbsolutePath() + File.separatorChar + getName() + File.separatorChar + GUID.generate(); - sourceStore = new FileContentStore(storeDir); + sourceStore = new FileContentStore(ctx, storeDir); // create the target file store storeDir = tempDir.getAbsolutePath() + File.separatorChar + getName() + File.separatorChar + GUID.generate(); - targetStore = new FileContentStore(storeDir); + targetStore = new FileContentStore(ctx, storeDir); // create the replicator replicator = new ContentStoreReplicator(); diff --git a/source/java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java b/source/java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java index 4cc8962cf4..d2e4d5efcb 100644 --- a/source/java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java +++ b/source/java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java @@ -70,13 +70,13 @@ public class ReplicatingContentStoreTest extends AbstractWritableContentStoreTes File tempDir = TempFileProvider.getTempDir(); // create a primary file store String storeDir = tempDir.getAbsolutePath() + File.separatorChar + GUID.generate(); - primaryStore = new FileContentStore(storeDir); + primaryStore = new FileContentStore(ctx, storeDir); // create some secondary file stores secondaryStores = new ArrayList(3); for (int i = 0; i < 4; i++) { storeDir = tempDir.getAbsolutePath() + File.separatorChar + GUID.generate(); - FileContentStore store = new FileContentStore(storeDir); + FileContentStore store = new FileContentStore(ctx, storeDir); secondaryStores.add(store); // Only the first 3 are writable if (i >= 3) diff --git a/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformer.java b/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformer.java index f7bf4da7b4..7fb4836ed5 100644 --- a/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformer.java +++ b/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2008 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,7 @@ * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing + * FLOSS exception. You should have received a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ @@ -32,6 +32,7 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.util.exec.RuntimeExec; +import org.alfresco.util.exec.RuntimeExec.ExecutionResult; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -53,6 +54,12 @@ public class ImageMagickContentTransformer extends AbstractImageMagickContentTra /** the system command executer */ private RuntimeExec executer; + + /** the check command executer */ + private RuntimeExec checkCommand; + + /** the output from the check command */ + private String versionString; /** * Default constructor @@ -79,6 +86,29 @@ public class ImageMagickContentTransformer extends AbstractImageMagickContentTra { this.executer = executer; } + + + /** + * Sets the command that must be executed in order to retrieve version information from the converting executable + * and thus test that the executable itself is present. + * + * @param checkCommand + * command executer to retrieve version information + */ + public void setCheckCommand(RuntimeExec checkCommand) + { + this.checkCommand = checkCommand; + } + + /** + * Gets the version string captured from the check command. + * + * @return the version string + */ + public String getVersionString() + { + return this.versionString; + } /** * Checks for the JMagick and ImageMagick dependencies, using the common @@ -92,6 +122,30 @@ public class ImageMagickContentTransformer extends AbstractImageMagickContentTra throw new AlfrescoRuntimeException("System runtime executer not set"); } super.init(); + if (isAvailable()) + { + try + { + ExecutionResult result = this.checkCommand.execute(); + if (result.getSuccess()) + { + this.versionString = result.getStdOut().trim(); + } + else + { + setAvailable(false); + } + } + catch (Throwable e) + { + setAvailable(false); + logger.error(getClass().getSimpleName() + " not available: " + + (e.getMessage() != null ? e.getMessage() : "")); + // debug so that we can trace the issue if required + logger.debug(e); + } + + } } /** diff --git a/source/java/org/alfresco/repo/content/transform/swf/PDFToSWFContentTransformer.java b/source/java/org/alfresco/repo/content/transform/swf/PDFToSWFContentTransformer.java index 4ed02ef1ce..5b377a3a5b 100644 --- a/source/java/org/alfresco/repo/content/transform/swf/PDFToSWFContentTransformer.java +++ b/source/java/org/alfresco/repo/content/transform/swf/PDFToSWFContentTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2008 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,7 @@ * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing + * FLOSS exception. You should have received a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ @@ -54,6 +54,9 @@ public class PDFToSWFContentTransformer extends AbstractContentTransformer2 /** Used to indicate whether the transformaer in available or not */ private boolean available = false; + /** Stores the output from the check command */ + private String versionString; + /** Check and transform command */ private RuntimeExec checkCommand; private RuntimeExec transformCommand; @@ -130,15 +133,13 @@ public class PDFToSWFContentTransformer extends AbstractContentTransformer2 { ExecutionResult result = getCheckCommand().execute(); // check the return code - this.available = result.getSuccess(); - if (this.available == false) + if (this.available = result.getSuccess()) { - logger.error("Failed to start SWF2PDF transformer: \n" + result); + this.versionString = result.getStdOut().trim(); } else { - // no check - just assume it is available - this.available = true; + logger.error("Failed to start SWF2PDF transformer: \n" + result); } // call the base class to make sure that it gets registered @@ -249,4 +250,24 @@ public class PDFToSWFContentTransformer extends AbstractContentTransformer2 return result; } + + /** + * Signals whether this transformer is available. + * + * @return true, if is available + */ + public boolean isAvailable() + { + return this.available; + } + + /** + * Gets the version string captured from the check command. + * + * @return the version string + */ + public String getVersionString() + { + return this.versionString; + } } diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorServiceAvailableEvent.java b/source/java/org/alfresco/repo/descriptor/DescriptorServiceAvailableEvent.java new file mode 100644 index 0000000000..b27065e64e --- /dev/null +++ b/source/java/org/alfresco/repo/descriptor/DescriptorServiceAvailableEvent.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.descriptor; + +import org.alfresco.service.descriptor.DescriptorService; +import org.springframework.context.ApplicationEvent; + +/** + * A class of event that notifies the listener of the availability of the {@link DescriptorService}. Useful for + * Monitoring purposes. + * + * @author dward + */ +public class DescriptorServiceAvailableEvent extends ApplicationEvent +{ + + private static final long serialVersionUID = 8217523101300405165L; + + /** + * The Constructor. + * + * @param source + * the source descriptor service + */ + public DescriptorServiceAvailableEvent(DescriptorService source) + { + super(source); + } + + /** + * Gets the descriptor service that raised the event. + * + * @return the descriptor service + */ + public DescriptorService getDescriptorService() + { + return (DescriptorService) getSource(); + } +} diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java index 1334730bdb..74a22921fa 100644 --- a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java +++ b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java @@ -183,6 +183,8 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean implements Desc }; installedRepoDescriptor = transactionService.getRetryingTransactionHelper().doInTransaction( createDescriptorWork, transactionService.isReadOnly(), false); + + ((ApplicationContext)event.getSource()).publishEvent(new DescriptorServiceAvailableEvent(this)); } /* diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java index 2986148e11..237a4eb270 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java @@ -70,7 +70,11 @@ import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; /** * This class is resource manager LuceneIndexers and LuceneSearchers. It supports two phase commit inside XA @@ -80,7 +84,8 @@ import org.springframework.beans.factory.InitializingBean; * @author andyh */ -public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneIndexerAndSearcher, XAResource +public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneIndexerAndSearcher, XAResource, + ApplicationContextAware { private static Log logger = LogFactory.getLog(AbstractLuceneIndexerAndSearcherFactory.class); @@ -180,6 +185,8 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI private boolean cacheEnabled = true; + private ConfigurableApplicationContext applicationContext; + /** * Private constructor for the singleton TODO: FIt in with IOC */ @@ -189,6 +196,26 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI super(); } + + /* + * (non-Javadoc) + * @seeorg.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context. + * ApplicationContext) + */ + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = (ConfigurableApplicationContext) applicationContext; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneConfig#getApplicationContext() + */ + public ConfigurableApplicationContext getApplicationContext() + { + return this.applicationContext; + } + /** * Set the directory that contains the indexes * @@ -995,7 +1022,6 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory implements LuceneI private Set factories; - @SuppressWarnings("unused") private NodeService nodeService; private String targetLocation; diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java index 28b8a8d663..460a2377eb 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneConfig.java @@ -28,6 +28,7 @@ import java.util.concurrent.ThreadPoolExecutor; import org.alfresco.repo.domain.hibernate.BulkLoader; import org.alfresco.repo.search.MLAnalysisMode; +import org.springframework.context.ConfigurableApplicationContext; public interface LuceneConfig { @@ -208,4 +209,10 @@ public interface LuceneConfig * @return */ public int getMaxLinkAspectCacheSize(); + + /** + * Gets the application context through which events can be broadcast + * @return + */ + public ConfigurableApplicationContext getApplicationContext(); } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexEvent.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexEvent.java new file mode 100644 index 0000000000..408ad2b903 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexEvent.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.search.impl.lucene.index; + +import org.springframework.context.ApplicationEvent; + +/** + * A class of event that notifies the listener of a significant event relating to a Lucene index. Useful for Monitoring + * purposes. + * + * @author dward + */ +public class IndexEvent extends ApplicationEvent +{ + + private static final long serialVersionUID = -4616231785087405506L; + + /** The event description. */ + private final String description; + + /** Its instance count. */ + private final int count; + + /** + * The Constructor. + * + * @param source + * the source index monitor + * @param description + * the event description + * @param count + * its instance count + */ + public IndexEvent(IndexMonitor source, String description, int count) + { + super(source); + this.description = description; + this.count = count; + } + + /** + * Gets the source index monitor. + * + * @return the index monitor + */ + public IndexMonitor getIndexMonitor() + { + return (IndexMonitor) getSource(); + } + + /** + * Gets the event description. + * + * @return the description + */ + public String getDescription() + { + return this.description; + } + + /** + * Gets the event instance count. + * + * @return the count + */ + public int getCount() + { + return this.count; + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java index 999191c0df..964b6ddefb 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java @@ -46,11 +46,13 @@ import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; +import java.util.TreeMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -84,6 +86,10 @@ import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.RAMDirectory; import org.safehaus.uuid.UUID; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; /** * The information that makes up an index. IndexInfoVersion Repeated information of the form @@ -118,7 +124,7 @@ import org.safehaus.uuid.UUID; * * @author Andy Hind */ -public class IndexInfo +public class IndexInfo implements IndexMonitor { public static final String MAIN_READER = "MainReader"; @@ -164,6 +170,11 @@ public class IndexInfo */ private File indexDirectory; + /** + * The directory relative to the root path + */ + private String relativePath; + /** * The file holding the index information */ @@ -312,7 +323,9 @@ public class IndexInfo private ThreadPoolExecutor threadPoolExecutor; private LuceneConfig config; - + + private List applicationListeners = new LinkedList(); + static { // We do not require any of the lucene in-built locking. @@ -426,6 +439,17 @@ public class IndexInfo throw new AlfrescoRuntimeException("The index must be held in a directory"); } + // Work out the relative path of the index + try + { + String indexRoot = new File(config.getIndexRootLocation()).getCanonicalPath(); + this.relativePath = this.indexDirectory.getCanonicalPath().substring(indexRoot.length() + 1); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Failed to determine index relative path", e); + } + // Create the info files. File indexInfoFile = new File(this.indexDirectory, INDEX_INFO); File indexInfoBackupFile = new File(this.indexDirectory, INDEX_INFO_BACKUP); @@ -628,6 +652,8 @@ public class IndexInfo cleaner.schedule(); } }, 0, 20000); + + publishDiscoveryEvent(); } private class DeleteUnknownGuidDirectories implements LockWork @@ -1501,6 +1527,7 @@ public class IndexInfo { throw new IndexerException("Invalid transition for " + id + " from " + entry.getStatus() + " to " + TransactionStatus.COMMITTED); } + notifyListeners("CommittedTransactions", 1); } public boolean requiresFileLock() @@ -3220,6 +3247,8 @@ public class IndexInfo } dumpInfo(); + + notifyListeners("MergedDeletions", toDelete.size()); return null; } @@ -3230,6 +3259,7 @@ public class IndexInfo } }); + } finally { @@ -3475,6 +3505,8 @@ public class IndexInfo registerReferenceCountingIndexReader(finalMergeTargetId, finalNewReader); + notifyListeners("MergedIndexes", toMerge.size()); + dumpInfo(); writeStatus(); @@ -3633,10 +3665,201 @@ public class IndexInfo } return false; } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#getRelativePath() + */ + public String getRelativePath() + { + return this.relativePath; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#getStatusSnapshot() + */ + public Map getStatusSnapshot() + { + Map snapShot = new TreeMap(); + readWriteLock.writeLock().lock(); + try + { + for (IndexEntry entry : indexEntries.values()) + { + String stateKey = entry.getType() + "-" + entry.getStatus(); + Integer count = snapShot.get(stateKey); + snapShot.put(stateKey, count == null ? 1 : count + 1); + } + return snapShot; + } + finally + { + readWriteLock.writeLock().unlock(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#getActualSize() + */ + public long getActualSize() throws IOException + { + getReadLock(); + try + { + int size = 0; + for (IndexEntry entry : this.indexEntries.values()) + { + File location = new File(this.indexDirectory, entry.getName()).getCanonicalFile(); + File[] contents = location.listFiles(); + for (File file : contents) + { + if (file.isFile()) + { + size += file.length(); + } + } + } + return size; + } + finally + { + releaseReadLock(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#getUsedSize() + */ + public long getUsedSize() throws IOException + { + getReadLock(); + try + { + return sizeRecurse(this.indexDirectory); + } + finally + { + releaseReadLock(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#getNumberOfDocuments() + */ + public int getNumberOfDocuments() throws IOException + { + IndexReader reader = getMainIndexReferenceCountingReadOnlyIndexReader(); + try + { + return reader.numDocs(); + } + finally + { + reader.close(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#getNumberOfFields() + */ + public int getNumberOfFields() throws IOException + { + + IndexReader reader = getMainIndexReferenceCountingReadOnlyIndexReader(); + try + { + return reader.getFieldNames(IndexReader.FieldOption.ALL).size(); + } + finally + { + reader.close(); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#getNumberOfIndexedFields() + */ + public int getNumberOfIndexedFields() throws IOException + { + IndexReader reader = getMainIndexReferenceCountingReadOnlyIndexReader(); + try + { + return reader.getFieldNames(IndexReader.FieldOption.INDEXED).size(); + } + finally + { + reader.close(); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.index.IndexMonitor#addApplicationListener(org.springframework.context. + * ApplicationListener) + */ + public void addApplicationListener(ApplicationListener listener) + { + this.applicationListeners.add(listener); + } + + private long sizeRecurse(File fileOrDir) + { + long size = 0; + if (fileOrDir.isDirectory()) + { + File[] files = fileOrDir.listFiles(); + for (File file : files) + { + size += sizeRecurse(file); + } + } + else + { + size = fileOrDir.length(); + } + return size; + } + + private void publishDiscoveryEvent() + { + final IndexEvent discoveryEvent = new IndexEvent(this, "Discovery", 1); + final ConfigurableApplicationContext applicationContext = this.config.getApplicationContext(); + try + { + applicationContext.publishEvent(discoveryEvent); + } + catch (IllegalStateException e) + { + // There's a possibility that the application context hasn't fully refreshed yet, so register a listener + // that will fire when it has + applicationContext.addApplicationListener(new ApplicationListener() + { + + public void onApplicationEvent(ApplicationEvent event) + { + if (event instanceof ContextRefreshedEvent) + { + applicationContext.publishEvent(discoveryEvent); + } + } + }); + } + } + + private void notifyListeners(String description, int count) + { + if (!this.applicationListeners.isEmpty()) + { + IndexEvent event = new IndexEvent(this, description, count); + for (ApplicationListener listener : this.applicationListeners) + { + listener.onApplicationEvent(event); + } + } + } interface Schedulable { void schedule(); } - } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexMonitor.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexMonitor.java new file mode 100644 index 0000000000..e0ea6051b0 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexMonitor.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.search.impl.lucene.index; + +import java.io.IOException; +import java.util.Map; + +import org.springframework.context.ApplicationListener; + +/** + * An interface that exposes information about a Lucene Index and that allows registration of a listener for event + * notifications. + * + * @author dward + */ +public interface IndexMonitor +{ + /** + * Gets the relative path of the index directory. + * + * @return the relative path + */ + public String getRelativePath(); + + /** + * Gets a snapshot of the statuses of the individual entries in this index. + * + * @return a map of entry status names to entry counts + */ + public Map getStatusSnapshot(); + + /** + * Gets the actual size of the index in bytes. + * + * @return the actual size in bytes + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public long getActualSize() throws IOException; + + /** + * Gets the size used on disk by the index directory. A large discrepancy from the value returned by + * {@link #getActualSize()} may indicate that there are unused data files. + * + * @return the size on disk in bytes + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public long getUsedSize() throws IOException; + + /** + * Gets the number of documents in the index. + * + * @return the number of documents + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public int getNumberOfDocuments() throws IOException; + + /** + * Gets the number of fields known to the index. + * + * @return the number of fields + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public int getNumberOfFields() throws IOException; + + /** + * Gets the number of indexed fields. + * + * @return the number of indexed fields + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public int getNumberOfIndexedFields() throws IOException; + + /** + * Registers a listener for events on this index. + * + * @param listener + * the listener + */ + public void addApplicationListener(ApplicationListener listener); +} diff --git a/source/java/org/alfresco/util/ApplicationContextHelper.java b/source/java/org/alfresco/util/ApplicationContextHelper.java index d188384ae2..7bc4984f17 100644 --- a/source/java/org/alfresco/util/ApplicationContextHelper.java +++ b/source/java/org/alfresco/util/ApplicationContextHelper.java @@ -24,7 +24,7 @@ */ package org.alfresco.util; -import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** @@ -46,7 +46,7 @@ public class ApplicationContextHelper * * @return Returns a new application context */ - public synchronized static ApplicationContext getApplicationContext() + public synchronized static ConfigurableApplicationContext getApplicationContext() { if (instance != null) { diff --git a/source/java/org/alfresco/util/OpenOfficeConnectionEvent.java b/source/java/org/alfresco/util/OpenOfficeConnectionEvent.java new file mode 100644 index 0000000000..133cba2428 --- /dev/null +++ b/source/java/org/alfresco/util/OpenOfficeConnectionEvent.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.util; + +import java.util.Map; + +import org.springframework.context.ApplicationEvent; + +/** + * A class of event that notifies the listener of the status of the Open Office Connection. Useful for Monitoring + * purposes. + * + * @author dward + */ +public class OpenOfficeConnectionEvent extends ApplicationEvent +{ + private static final long serialVersionUID = 8834274840220309384L; + + /** + * The Constructor. + * + * @param metaData + * the meta data map + */ + public OpenOfficeConnectionEvent(Map metaData) + { + super(metaData); + } + + /** + * Gets the meta data map. + * + * @return the meta data map + */ + @SuppressWarnings("unchecked") + public Map getMetaData() + { + return (Map) getSource(); + } +} diff --git a/source/java/org/alfresco/util/OpenOfficeConnectionTester.java b/source/java/org/alfresco/util/OpenOfficeConnectionTester.java index 932e969eee..b85e91e578 100644 --- a/source/java/org/alfresco/util/OpenOfficeConnectionTester.java +++ b/source/java/org/alfresco/util/OpenOfficeConnectionTester.java @@ -24,8 +24,12 @@ */ package org.alfresco.util; +import java.lang.reflect.Method; import java.net.ConnectException; +import java.util.Map; +import java.util.TreeMap; +import net.sf.jooreports.openoffice.connection.AbstractOpenOfficeConnection; import net.sf.jooreports.openoffice.connection.OpenOfficeConnection; import org.alfresco.error.AlfrescoRuntimeException; @@ -37,8 +41,14 @@ import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; +import com.sun.star.registry.RegistryValueType; +import com.sun.star.registry.XRegistryKey; +import com.sun.star.registry.XSimpleRegistry; +import com.sun.star.uno.UnoRuntime; + /** * A bootstrap class that checks for the presence of a valid OpenOffice connection, as provided by the * net.sf.jooreports.openoffice.connection.OpenOfficeConnection implementations. @@ -47,6 +57,7 @@ import org.springframework.context.ApplicationEvent; */ public class OpenOfficeConnectionTester extends AbstractLifecycleBean { + private static final String ATTRIBUTE_AVAILABLE = "available"; private static final String INFO_CONNECTION_VERIFIED = "system.openoffice.info.connection_verified"; private static final String ERR_CONNECTION_FAILED = "system.openoffice.err.connection_failed"; private static final String ERR_CONNECTION_LOST = "system.openoffice.err.connection_lost"; @@ -55,6 +66,7 @@ public class OpenOfficeConnectionTester extends AbstractLifecycleBean private static Log logger = LogFactory.getLog(OpenOfficeConnectionTester.class); private OpenOfficeConnection connection; + private Map openOfficeMetadata = new TreeMap(); private boolean strict; public OpenOfficeConnectionTester() @@ -87,6 +99,7 @@ public class OpenOfficeConnectionTester extends AbstractLifecycleBean protected void onBootstrap(ApplicationEvent event) { checkConnection(); + ((ApplicationContext) event.getSource()).publishEvent(new OpenOfficeConnectionEvent(this.openOfficeMetadata)); } /** @@ -109,7 +122,7 @@ public class OpenOfficeConnectionTester extends AbstractLifecycleBean * then a disconnected {@link #setConnection(OpenOfficeConnection) connection} will result in a * runtime exception being generated. */ - private synchronized void checkConnection() + private void checkConnection() { String connectedMessage = I18NUtil.getMessage(INFO_CONNECTION_VERIFIED); boolean connected = testAndConnect(); @@ -134,24 +147,66 @@ public class OpenOfficeConnectionTester extends AbstractLifecycleBean public boolean testAndConnect() { - PropertyCheck.mandatory(this, "connection", connection); - if (connection.isConnected()) + synchronized (this.openOfficeMetadata) { - // the connection is fine - return true; - } - // attempt to make the connection - try - { - connection.connect(); - // that worked - return true; - } - catch (ConnectException e) - { - // No luck - return false; + PropertyCheck.mandatory(this, "connection", connection); + if (!connection.isConnected()) + { + try + { + connection.connect(); + } + catch (ConnectException e) + { + // No luck + this.openOfficeMetadata.clear(); + this.openOfficeMetadata.put(ATTRIBUTE_AVAILABLE, Boolean.FALSE); + return false; + } + } + + // Let's try to get at the version metadata + Boolean lastAvailability = (Boolean)this.openOfficeMetadata.get(ATTRIBUTE_AVAILABLE); + if (lastAvailability == null || !lastAvailability.booleanValue()) + { + this.openOfficeMetadata.put(ATTRIBUTE_AVAILABLE, Boolean.TRUE); + try + { + // We have to peak inside the connection class to get the service we want! + Method getServiceMethod = AbstractOpenOfficeConnection.class.getDeclaredMethod("getService", + String.class); + getServiceMethod.setAccessible(true); + Object configurationRegistry = getServiceMethod.invoke(connection, + "com.sun.star.configuration.ConfigurationRegistry"); + XSimpleRegistry registry = (XSimpleRegistry) UnoRuntime.queryInterface( + com.sun.star.registry.XSimpleRegistry.class, configurationRegistry); + registry.open("org.openoffice.Setup", true, false); + XRegistryKey root = registry.getRootKey(); + XRegistryKey product = root.openKey("Product"); + for (XRegistryKey key : product.openKeys()) + { + switch (key.getValueType().getValue()) + { + case RegistryValueType.LONG_value: + openOfficeMetadata.put(key.getKeyName(), key.getLongValue()); + break; + case RegistryValueType.ASCII_value: + openOfficeMetadata.put(key.getKeyName(), key.getAsciiValue()); + break; + case RegistryValueType.STRING_value: + openOfficeMetadata.put(key.getKeyName(), key.getStringValue()); + break; + } + } + registry.close(); + } + catch (Exception e) + { + logger.warn("Error trying to query Open Office version information", e); + } + } } + return true; } /** diff --git a/source/java/org/alfresco/util/ResourceFinder.java b/source/java/org/alfresco/util/ResourceFinder.java new file mode 100644 index 0000000000..44ef681d6b --- /dev/null +++ b/source/java/org/alfresco/util/ResourceFinder.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2008 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.util; + +import java.io.IOException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +/** + * Can be used in Spring configuration to search for all resources matching an array of patterns. + * + * @author dward + */ +public class ResourceFinder extends PathMatchingResourcePatternResolver +{ + /** + * Instantiates a new resource finder. + */ + public ResourceFinder() + { + } + + /** + * The Constructor. + * + * @param classLoader + * the class loader + */ + public ResourceFinder(ClassLoader classLoader) + { + super(classLoader); + } + + /** + * The Constructor. + * + * @param resourceLoader + * the resource loader + */ + public ResourceFinder(ResourceLoader resourceLoader) + { + super(resourceLoader); + } + + /** + * Gets an array of resources matching the given location patterns. + * + * @param locationPatterns + * the location patterns + * @return the matching resources, ordered by locationPattern index and location in the classpath + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public Resource[] getResources(String[] locationPatterns) throws IOException + { + List resources = new LinkedList(); + for (String locationPattern : locationPatterns) + { + resources.addAll(Arrays.asList(getResources(locationPattern))); + } + Resource[] resourceArray = new Resource[resources.size()]; + resources.toArray(resourceArray); + return resourceArray; + } +}