You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by Glen Daniels <gl...@thoughtcraft.com> on 2009/10/14 05:26:44 UTC
Re: svn commit: r824930 - in
/webservices/commons/trunk/modules/axiom/modules:
axiom-api/src/main/java/org/apache/axiom/attachments/
axiom-tests/src/test/java/org/apache/axiom/attachments/
Hi Rich!
This change apparently broke the Hudson build:
http://hudson.zones.apache.org/hudson/job/ws-axiom-trunk/72/
Could you look into this?
Thanks,
--Glen
scheu@apache.org wrote:
> Author: scheu
> Date: Tue Oct 13 21:17:42 2009
> New Revision: 824930
>
> URL: http://svn.apache.org/viewvc?rev=824930&view=rev
> Log:
> WSCOMMONS-506
> Contributor: Wendy Raschke
> Added a property to ensure that attachment files are deleted.
> Added a validation test.
>
> Added:
> webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentCacheMonitor.java
> Modified:
> webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/CachedFileDataSource.java
> webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java
>
> Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentCacheMonitor.java
> URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentCacheMonitor.java?rev=824930&view=auto
> ==============================================================================
> --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentCacheMonitor.java (added)
> +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentCacheMonitor.java Tue Oct 13 21:17:42 2009
> @@ -0,0 +1,303 @@
> +/*
> + * Copyright 2004, 2009 The Apache Software Foundation.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +package org.apache.axiom.attachments;
> +
> +import java.util.Map;
> +import java.util.HashMap;
> +import java.util.Iterator;
> +import java.util.Timer;
> +import java.util.TimerTask;
> +
> +import java.io.File;
> +
> +import java.io.IOException;
> +import java.security.AccessController;
> +import java.security.PrivilegedAction;
> +
> +import org.apache.commons.logging.Log;
> +import org.apache.commons.logging.LogFactory;
> +
> +/**
> + * The CacheMonitor is responsible for deleting temporary attachment files
> + * after a timeout period has expired.
> + *
> + * The register method is invoked when the attachment file is created.
> + * The access method is invoked whenever the attachment file is accessed.
> + * The checkForAgedFiles method is invoked whenever the monitor should look for
> + * files to cleanup (delete).
> + *
> + */
> +public final class AttachmentCacheMonitor {
> +
> + static Log log =
> + LogFactory.getLog(AttachmentCacheMonitor.class.getName());
> +
> + // Setting this property puts a limit on the lifetime of a cache file
> + // The default is "0", which is interpreted as forever
> + // The suggested value is 300 seconds
> + private int attachmentTimeoutSeconds = 0; // Default is 0 (forever)
> + private int refreshSeconds = 0;
> + public static final String ATTACHMENT_TIMEOUT_PROPERTY = "org.apache.axiom.attachments.tempfile.expiration";
> +
> + // HashMap
> + // Key String = Absolute file name
> + // Value Long = Last Access Time
> + private HashMap files = new HashMap();
> +
> + // Delete detection is batched
> + private Long priorDeleteMillis = getTime();
> +
> + private Timer timer = null;
> +
> + private static AttachmentCacheMonitor _singleton = null;
> +
> +
> + /**
> + * Get or Create an AttachmentCacheMonitor singleton
> + * @return
> + */
> + public static synchronized AttachmentCacheMonitor getAttachmentCacheMonitor() {
> + if (_singleton == null) {
> + _singleton = new AttachmentCacheMonitor();
> + }
> + return _singleton;
> + }
> +
> + /**
> + * Constructor
> + * Intentionally private. Callers should use getAttachmentCacheMonitor
> + * @see getAttachmentCacheMonitor
> + */
> + private AttachmentCacheMonitor() {
> + String value = "";
> + try {
> + value = System.getProperty(ATTACHMENT_TIMEOUT_PROPERTY, "0");
> + attachmentTimeoutSeconds = Integer.valueOf(value).intValue();
> + } catch (Throwable t) {
> + // Swallow exception and use default, but log a warning message
> + if (log.isDebugEnabled()) {
> + log.debug("The value of " + value + " was not valid. The default " +
> + attachmentTimeoutSeconds + " will be used instead.");
> + }
> + }
> + refreshSeconds = attachmentTimeoutSeconds / 2;
> +
> + if (log.isDebugEnabled()) {
> + log.debug("Custom Property Key = " + ATTACHMENT_TIMEOUT_PROPERTY);
> + log.debug(" Value = " + attachmentTimeoutSeconds);
> + }
> +
> + if (refreshSeconds > 0) {
> + timer = new Timer( true );
> + timer.schedule( new CleanupFilesTask(),
> + refreshSeconds * 1000,
> + refreshSeconds * 1000 );
> + }
> + }
> +
> + /**
> + * @return timeout value in seconds
> + */
> + public synchronized int getTimeout() {
> + return attachmentTimeoutSeconds;
> + }
> +
> + /**
> + * This method should
> + * Set a new timeout value
> + * @param timeout new timeout value in seconds
> + */
> + public synchronized void setTimeout(int timeout) {
> + // If the setting to the same value, simply return
> + if (timeout == attachmentTimeoutSeconds) {
> + return;
> + }
> +
> + attachmentTimeoutSeconds = timeout;
> +
> + // Reset the refresh
> + refreshSeconds = attachmentTimeoutSeconds / 2;
> +
> + // Make sure to cancel the prior timer
> + if (timer != null) {
> + timer.cancel(); // Remove scheduled tasks from the prior timer
> + timer = null;
> + }
> +
> + // Make a new timer if necessary
> + if (refreshSeconds > 0) {
> + timer = new Timer( true );
> + timer.schedule( new CleanupFilesTask(),
> + refreshSeconds * 1000,
> + refreshSeconds * 1000 );
> + }
> +
> + if (log.isDebugEnabled()) {
> + log.debug("New timeout = " + attachmentTimeoutSeconds);
> + log.debug("New refresh = " + refreshSeconds);
> + }
> + }
> +
> + /**
> + * Register a file name with the monitor.
> + * This will allow the Monitor to remove the file after
> + * the timeout period.
> + * @param fileName
> + */
> + public void register(String fileName) {
> + if (attachmentTimeoutSeconds > 0) {
> + _register(fileName);
> + _checkForAgedFiles();
> + }
> + }
> +
> + /**
> + * Indicates that the file was accessed.
> + * @param fileName
> + */
> + public void access(String fileName) {
> + if (attachmentTimeoutSeconds > 0) {
> + _access(fileName);
> + _checkForAgedFiles();
> + }
> + }
> +
> + /**
> + * Check for aged files and remove the aged ones.
> + */
> + public void checkForAgedFiles() {
> + if (attachmentTimeoutSeconds > 0) {
> + _checkForAgedFiles();
> + }
> + }
> +
> + private synchronized void _register(String fileName) {
> + Long currentTime = getTime();
> + if (log.isDebugEnabled()) {
> + log.debug("Register file " + fileName);
> + log.debug("Time = " + currentTime);
> + }
> + files.put(fileName, currentTime);
> + }
> +
> + private synchronized void _access(String fileName) {
> + Long currentTime = getTime();
> + Long priorTime = (Long) files.get(fileName);
> + if (priorTime != null) {
> + files.put(fileName, currentTime);
> + if (log.isDebugEnabled()) {
> + log.debug("Access file " + fileName);
> + log.debug("Old Time = " + priorTime);
> + log.debug("New Time = " + currentTime);
> + }
> + } else {
> + if (log.isDebugEnabled()) {
> + log.debug("The following file was already deleted and is no longer available: " +
> + fileName);
> + log.debug("The value of " + ATTACHMENT_TIMEOUT_PROPERTY +
> + " is " + attachmentTimeoutSeconds);
> + }
> + }
> + }
> +
> + private synchronized void _checkForAgedFiles() {
> + Long currentTime = getTime();
> + // Don't keep checking the map, only trigger
> + // the checking if it is plausible that
> + // files will need to be deleted.
> + // I chose a value of ATTACHMENTT_TIMEOUT_SECONDS/4
> + if (isExpired(priorDeleteMillis,
> + currentTime,
> + refreshSeconds)) {
> + Iterator it = files.keySet().iterator();
> + while (it.hasNext()) {
> + String fileName = (String) it.next();
> + Long lastAccess = (Long) files.get(fileName);
> + if (isExpired(lastAccess,
> + currentTime,
> + attachmentTimeoutSeconds)) {
> +
> + if (log.isDebugEnabled()) {
> + log.debug("Expired file " + fileName);
> + log.debug("Old Time = " + lastAccess);
> + log.debug("New Time = " + currentTime);
> + log.debug("Elapsed Time (ms) = " +
> + (currentTime.longValue() - lastAccess.longValue()));
> + }
> +
> + deleteFile(fileName);
> + // Use the iterator to remove this
> + // file from the map (this avoids
> + // the dreaded ConcurrentModificationException
> + it.remove();
> + }
> + }
> +
> + // Reset the prior delete time
> + priorDeleteMillis = currentTime;
> + }
> + }
> +
> + private boolean deleteFile(final String fileName ) {
> + Boolean privRet = (Boolean) AccessController.doPrivileged(new PrivilegedAction() {
> + public Object run() {
> + return _deleteFile(fileName);
> + }
> + });
> + return privRet.booleanValue();
> + }
> +
> + private Boolean _deleteFile(String fileName) {
> + boolean ret = false;
> + File file = new File(fileName);
> + if (file.exists()) {
> + ret = file.delete();
> + if (log.isDebugEnabled()) {
> + log.debug("Deletion Successful ? " + ret);
> + }
> + } else {
> + if (log.isDebugEnabled()) {
> + log.debug("This file no longer exists = " + fileName);
> + }
> + }
> + return new Boolean(ret);
> + }
> +
> +
> + private Long getTime() {
> + return new Long(System.currentTimeMillis());
> + }
> +
> + private boolean isExpired (Long oldTimeMillis,
> + Long newTimeMillis,
> + int thresholdSecs) {
> + long elapse = newTimeMillis.longValue() -
> + oldTimeMillis.longValue();
> + return (elapse > (thresholdSecs*1000));
> + }
> +
> +
> + private class CleanupFilesTask extends TimerTask {
> +
> + /**
> + * Trigger a checkForAgedFiles event
> + */
> + public void run() {
> + checkForAgedFiles();
> + }
> + }
> +}
>
> Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/CachedFileDataSource.java
> URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/CachedFileDataSource.java?rev=824930&r1=824929&r2=824930&view=diff
> ==============================================================================
> --- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/CachedFileDataSource.java (original)
> +++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/CachedFileDataSource.java Tue Oct 13 21:17:42 2009
> @@ -22,12 +22,45 @@
> import javax.activation.FileDataSource;
> import java.io.File;
>
> +import org.apache.commons.logging.Log;
> +import org.apache.commons.logging.LogFactory;
> +
> +
> public class CachedFileDataSource extends FileDataSource {
>
> String contentType = null;
> +
> + protected static Log log = LogFactory.getLog(CachedFileDataSource.class);
> +
> + // The AttachmentCacheMonitor is used to delete expired copies of attachment files.
> + private static AttachmentCacheMonitor acm =
> + AttachmentCacheMonitor.getAttachmentCacheMonitor();
> +
> + // Represents the absolute pathname of cached attachment file
> + private String cachedFileName = null;
>
> public CachedFileDataSource(File arg0) {
> super(arg0);
> + if (log.isDebugEnabled()) {
> + log.debug("Enter CachedFileDataSource ctor");
> + }
> + if (arg0 != null) {
> + try {
> + cachedFileName = arg0.getCanonicalPath();
> + } catch (java.io.IOException e) {
> + log.error("IOException caught: " + e);
> + }
> + }
> + if (cachedFileName != null) {
> + if (log.isDebugEnabled()) {
> + log.debug("Cached file: " + cachedFileName);
> + log.debug("Registering the file with AttachmentCacheMonitor and also marked it as being accessed");
> + }
> + // Tell the monitor that the file is being accessed.
> + acm.access(cachedFileName);
> + // Register the file with the AttachmentCacheMonitor
> + acm.register(cachedFileName);
> + }
> }
>
> public String getContentType() {
>
> Modified: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java
> URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java?rev=824930&r1=824929&r2=824930&view=diff
> ==============================================================================
> --- webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java (original)
> +++ webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java Tue Oct 13 21:17:42 2009
> @@ -19,6 +19,7 @@
>
> package org.apache.axiom.attachments;
>
> +import org.apache.axiom.attachments.AttachmentCacheMonitor;
> import org.apache.axiom.attachments.utils.IOUtils;
> import org.apache.axiom.om.AbstractTestCase;
> import org.apache.axiom.om.OMElement;
> @@ -36,6 +37,7 @@
> import java.io.BufferedReader;
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> +import java.io.File;
> import java.io.IOException;
> import java.io.InputStream;
> import java.io.InputStreamReader;
> @@ -380,6 +382,78 @@
> assertTrue("Expected MessageContent Length of " + fileSize + " but received " + length,
> length == fileSize);
> }
> +
> + public void testCachedFilesExpired() throws Exception {
> +
> + // Set file expiration to 10 seconds
> + long INTERVAL = 5 * 1000; // 5 seconds for Thread to sleep
> + Thread t = Thread.currentThread();
> +
> +
> + // Get the AttachmentCacheMonitor and force it to remove files after
> + // 10 seconds.
> + AttachmentCacheMonitor acm = AttachmentCacheMonitor.getAttachmentCacheMonitor();
> + int previousTime = acm.getTimeout();
> +
> + try {
> + acm.setTimeout(10);
> +
> +
> + File aFile = new File("A");
> + aFile.createNewFile();
> + String aFileName = aFile.getCanonicalPath();
> + acm.register(aFileName);
> +
> + t.sleep(INTERVAL);
> +
> + File bFile = new File("B");
> + bFile.createNewFile();
> + String bFileName = bFile.getCanonicalPath();
> + acm.register(bFileName);
> +
> + t.sleep(INTERVAL);
> +
> + acm.access(aFileName);
> +
> + // time since file A registration <= cached file expiration
> + assertTrue("File A should still exist", aFile.exists());
> +
> + t.sleep(INTERVAL);
> +
> + acm.access(bFileName);
> +
> + // time since file B registration <= cached file expiration
> + assertTrue("File B should still exist", bFile.exists());
> +
> + t.sleep(INTERVAL);
> +
> + File cFile = new File("C");
> + cFile.createNewFile();
> + String cFileName = cFile.getCanonicalPath();
> + acm.register(cFileName);
> + acm.access(bFileName);
> +
> + t.sleep(INTERVAL);
> +
> + acm.checkForAgedFiles();
> +
> + // time since file C registration <= cached file expiration
> + assertTrue("File C should still exist", cFile.exists());
> +
> + t.sleep(10* INTERVAL); // Give task loop time to delete aged files
> +
> +
> + // All files should be gone by now
> + assertFalse("File A should no longer exist", aFile.exists());
> + assertFalse("File B should no longer exist", bFile.exists());
> + assertFalse("File C should no longer exist", cFile.exists());
> + } finally {
> +
> + // Reset the timeout to the previous value so that no
> + // other tests are affected
> + acm.setTimeout(previousTime);
> + }
> + }
>
> /**
> * Returns the contents of the input stream as byte array.
>
>